﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Natur.MapManager
{
    public enum SectorFlag { ModifiedVertices = 0 }
    public class Map
    {
        const float THRESH_BOUNCE = .01f;
        const float THRESH_INTENSITY = .01f;
        const float THRESH_OVERLAP = .75f;

        public const int MAX_WALLS = 128;
        const float STEADY_GROUND = .5f;

        // Models
        const int MODEL_SPHERE = 0;
        const int MODEL_CUBE = 1;
        const int MODEL_CYLINDER = 2;
        const int MODEL_COUNT = 3;

        // Effects
        const int EFFECT_LIGHT = 0;
        const int EFFECT_BOUND = 1;
        const int EFFECT_LOCATION = 2;
        const int EFFECT_SPAWN = 3;
        const int EFFECT_RELAY = 4;
        const int EFFECT_COUNT = 5;

        // Bounding Planes
        const int PLANE_FORWARD = 0;
        const int PLANE_BACKWARD = 1;
        const int PLANE_LEFT = 2;
        const int PLANE_RIGHT = 3;
        const int PLANE_UP = 4;
        const int PLANE_DOWN = 5;

        private GraphicsDevice mapDevice;
        private ContentManager mapManager;
        
        private RasterizerState stateWireframe;
        private RasterizerState stateNoCull;

        Model[] aryModels = new Model[MODEL_COUNT];
        Effect[] aryEffects = new Effect[EFFECT_COUNT];

        private List<Sector> lstSectors = new List<Sector>(1);

        public static float GridSize = .01f;
        private static Plane[] aryBoundingPlanes = new Plane[6];

        public Map(GraphicsDevice newDevice, IServiceProvider contentServices, float newGridSize)
        {
            mapDevice = newDevice;
            mapManager = new ContentManager(contentServices, "Content");
            GridSize = newGridSize;

            // Graphics states
            stateWireframe = new RasterizerState();
            stateWireframe.FillMode = FillMode.WireFrame;
            
            stateNoCull = new RasterizerState();
            stateNoCull.CullMode = CullMode.None;

            // Load Models
            aryModels[MODEL_SPHERE] = mapManager.Load<Model>("Models\\Sphere");
            aryModels[MODEL_CUBE] = mapManager.Load<Model>("Models\\Cube");
            aryModels[MODEL_CYLINDER] = mapManager.Load<Model>("Models\\Cylinder");

            // Load Effects
            aryEffects[EFFECT_LIGHT] = mapManager.Load<Effect>("Effects\\mapLight");
            aryEffects[EFFECT_BOUND] = mapManager.Load<Effect>("Effects\\mapBound");
            aryEffects[EFFECT_LOCATION] = mapManager.Load<Effect>("Effects\\mapColorObject");
            aryEffects[EFFECT_SPAWN] = aryEffects[EFFECT_LOCATION].Clone();
            aryEffects[EFFECT_RELAY] = aryEffects[EFFECT_LOCATION].Clone();

            aryEffects[EFFECT_BOUND].Parameters["objectColor"].SetValue(new Vector4(1, 0, 0, .35f));
            aryEffects[EFFECT_LOCATION].Parameters["objectColor"].SetValue(new Vector4(0, 0, 1, .35f));
            aryEffects[EFFECT_SPAWN].Parameters["objectColor"].SetValue(new Vector4(1, 1, 0, .6f));
            aryEffects[EFFECT_RELAY].Parameters["objectColor"].SetValue(new Vector4(.75f, 0, .75f, .6f));

            // Prepare physics
            aryBoundingPlanes[PLANE_FORWARD] = new Plane(Vector3.Forward, 1);
            aryBoundingPlanes[PLANE_BACKWARD] = new Plane(Vector3.Backward, 1);

            aryBoundingPlanes[PLANE_LEFT] = new Plane(Vector3.Left, 1);
            aryBoundingPlanes[PLANE_RIGHT] = new Plane(Vector3.Right, 1);

            aryBoundingPlanes[PLANE_UP] = new Plane(Vector3.Up, 1);
            aryBoundingPlanes[PLANE_DOWN] = new Plane(Vector3.Down, 1);
        }

        // ****** ACCESSROS ******

        public Animation GetAnimation(int sectorIndex, int animationIndex)
        { return lstSectors[sectorIndex].Animations[animationIndex]; }
        public Animation[] GetAnimations(int sectorIndex) { return lstSectors[sectorIndex].Animations.ToArray(); }
        public static DimensionedStructure GetDimensionedStructure(MapEntity fromSelected)
        {
            Vector3 newPosition = Vector3.Zero, newForward = Vector3.Forward, newUp = Vector3.Up;
            float newW = GridSize, newH = GridSize, newD = GridSize;

            // Position and Orientation
            if (fromSelected is IObject)
            { newPosition = ((IObject)fromSelected).Position; }
            if (fromSelected is IOriented)
            {
                newForward = ((IOriented)fromSelected).Forward;
                newUp = ((IOriented)fromSelected).Up;
            }

            // Dimensions
            // if pinned collect all
            if (fromSelected is IPin)
            {
                List<MapEntity> lstPinned = new List<MapEntity>(((IPin)fromSelected).Pinned);
                bool collectingPinned = true;
                while (collectingPinned)
                {
                    for (int i = 0; i < lstPinned.Count; i++)
                    {
                        bool pinFound = false;
                        // recurse any remaining pins
                        // add pinned and remove pin
                        if (lstPinned[i] is IPin)
                        {
                            lstPinned.AddRange(((IPin)lstPinned[i]).Pinned);
                            lstPinned.Remove(lstPinned[i]);
                            pinFound = true;
                        }
                        // exit on no pins
                        if (!pinFound) { collectingPinned = false; }
                    }
                }

                // Collect average dimension
                Matrix pinWorld = Matrix.CreateWorld(newPosition, newForward, newUp);
                Vector3 maxDimension = Vector3.Zero;
                Vector3 minDimension = Vector3.Zero;
                for (int i = 0; i < lstPinned.Count; i++)
                {
                    if (lstPinned[i] is IObject)
                    {
                        Vector3 newMax = Vector3.Transform(((IObject)lstPinned[i]).Position, Matrix.Invert(pinWorld));
                        Vector3 newMin = newMax;

                        if (lstPinned[i] is IDimensioned)
                        {
                            newMax += ((IDimensioned)lstPinned[i]).Max;
                            newMin += -((IDimensioned)lstPinned[i]).Max;
                        }
                        else if (lstPinned[i] is IRadial)
                        {
                            newMax += ((IRadial)lstPinned[i]).Max;
                            newMin += -((IRadial)lstPinned[i]).Max;
                        }
                        // if new max
                        if (newMax.X > maxDimension.X) { maxDimension.X = newMax.X; }
                        if (newMax.Y > maxDimension.Y) { maxDimension.Y = newMax.Y; }
                        if (newMax.Z > maxDimension.Z) { maxDimension.Z = newMax.Z; }

                        // if new min
                        if (newMin.X < minDimension.X) { minDimension.X = newMin.X; }
                        if (newMin.Y < minDimension.Y) { minDimension.Y = newMin.Y; }
                        if (newMin.Z < minDimension.Z) { minDimension.Z = newMin.Z; }
                    }
                }
                // Finalize
                newW = (maxDimension.X - minDimension.X) / 2;
                newH = (maxDimension.Y - minDimension.Y) / 2;
                newD = (maxDimension.Z - minDimension.Z) / 2;
            }
            // else standard
            else
            {
                if (fromSelected is IDimensioned)
                {
                    newW = ((IDimensioned)fromSelected).WRadius;
                    newH = ((IDimensioned)fromSelected).HRadius;
                    newD = ((IDimensioned)fromSelected).DRadius;
                }
                else if (fromSelected is IRadial)
                {
                    newW = ((IRadial)fromSelected).Radius;
                    newH = ((IRadial)fromSelected).Radius;
                    newD = ((IRadial)fromSelected).Radius;
                }
            }
            return new DimensionedStructure(ObjectType.Empty, newPosition, newForward, newUp, newW, newH, newD);
        }
        public Sector this[int sectorIndex] { get { return lstSectors[sectorIndex]; } }
        public int SectorCount { get { return lstSectors.Count; } }
        public Effect GetSectorEffect(int sectorIndex) { return lstSectors[sectorIndex].SectorEffect; }

        // ****** USER METHODS ******
        
        public int AddSector(int newTextureIndex, Effect newEffect)
        { lstSectors.Add(new Sector(newEffect, newTextureIndex)); return lstSectors.Count - 1; }

        public void BakeLights()
        {
            // Collect Lights
            List<Light> lstBakeLights = new List<Light>();
            for (int i = 0; i < lstSectors.Count; i++)
            {
                Light[] aryLights = lstSectors[i].Lights.ToArray();
                if (aryLights.Length > 0)
                {
                    for (int a = 0; a < aryLights.Length; a++)
                    { lstBakeLights.Add(aryLights[a]);}
                }
            }

            Color defaultColor = new Color(30, 30, 30, 255);
            for (int pS = 0; pS < lstSectors.Count; pS++)
            {
                MapEntity[] aryEntities = lstSectors[pS].Entities.ToArray();
                for (int i = 0; i < aryEntities.Length; i++)
                {
                    if (aryEntities[i] is IVertexed)
                    {
                        CustomVD[] aryVerticies = lstSectors[pS].Vertices.ToArray();
                        for (int a = ((IVertexed)aryEntities[i]).StartVertex; a < ((IVertexed)aryEntities[i]).StartVertex + ((IVertexed)aryEntities[i]).VertexCount; a++)
                        {
                            CustomVD vertHold = aryVerticies[a];
                            float vertIntendity = 0;
                            vertHold.LightColor = defaultColor;

                            for (int b = 0; b < lstBakeLights.Count; b++)
                            {
                                Vector3 lightDirection = Vector3.Normalize(vertHold.Position - lstBakeLights[b].Position);
                                float lightDistance = Vector3.Distance(vertHold.Position, lstBakeLights[b].Position);
                                float lightModifier = lstBakeLights[b].Intensity / (lightDistance);// * lightDistance);

                                Ray vertRay = new Ray(lstBakeLights[b].Position, lightDirection);
                                //MapEntity nearestStructure;
                                //float nearestBlock = IntersectsStructure(vertRay, out nearestStructure);

                                //if ((nearestStructure == null) || (nearestStructure == Entities[i]) || (nearestBlock > lightDistance * THRESH_OVERLAP))
                                //{
                                Color newColor = vertHold.LightColor;
                                float dotAdjust = Vector3.Dot(lightDirection, vertHold.Normal);

                                //if (dotAdjust < 0) { dotAdjust = 0; }
                                lightModifier *= 1 - dotAdjust;
                                vertIntendity += lightModifier;
                                //vertHold.LightColor = newColor;
                                //}
                            }
                            vertHold.LightColor = new Color(vertIntendity, vertIntendity, vertIntendity, 255);
                            aryVerticies[a] = vertHold;
                        }
                        lstSectors[pS].Vertices = new FlagList<CustomVD>(aryVerticies);
                    } // if vertexed
                } // for Selectables
            }
        }

        public CollisionData[] Collision(BoundingSphere searchSphere, Vector3 sphereVelocity, float sphereFriction, bool accessToTiggers, SelectableObject referenceID)
        {
            List<CollisionData> returnData = new List<CollisionData>(2);
            for (int i = 0; i < lstSectors.Count; i++)
            {
                {
                    CollisionData sectorData = lstSectors[i].Collision(searchSphere, sphereVelocity, sphereFriction, accessToTiggers, referenceID);
                    if (sectorData.Collision)
                    { returnData.Add(sectorData); }
                }
            }
            return returnData.ToArray();
        }

        public void Draw(GraphicsDevice drawDevice, Matrix viewMat, Matrix projMat, Vector3 cameraDirection, bool showBounds, bool showConstruction, bool showLights, bool showLocation)
        {
            // Set general effect parameters
            for (int i = 0; i < aryEffects.Length; i++)
            {
                aryEffects[i].Parameters["view"].SetValue(viewMat);
                aryEffects[i].Parameters["projection"].SetValue(projMat);
            }

            // Draw sectors
            for (int i = 0; i < lstSectors.Count; i++)
            {
                if (lstSectors[i].IsVisible)
                {
                    // Set effects
                    lstSectors[i].SectorEffect.Parameters["view"].SetValue(viewMat);
                    lstSectors[i].SectorEffect.Parameters["projection"].SetValue(projMat);
                    lstSectors[i].SectorEffect.Parameters["world"].SetValue(Matrix.Identity);
                    lstSectors[i].SectorEffect.Parameters["cameraDirection"].SetValue(cameraDirection);

                    // Draw lights
                    if (showLights)
                    {
                        for (int a = 0; a < lstSectors[i].Lights.Count; a++)
                        {
                            Light lightHold = lstSectors[i].Lights[a];
                            aryEffects[EFFECT_LIGHT].Parameters["lightWorld"].SetValue(Matrix.CreateScale(lightHold.Radius)
                                * Matrix.CreateWorld(lightHold.Position, lightHold.Forward, lightHold.Up));
                            aryEffects[EFFECT_LIGHT].Parameters["lightColor"].SetValue(lightHold.Color.ToVector4());
                            aryEffects[EFFECT_LIGHT].Parameters["lightDirection"].SetValue(lightHold.Forward);
                            aryEffects[EFFECT_LIGHT].Parameters["lightRange"].SetValue(lightHold.Range);

                            aryModels[MODEL_SPHERE].Meshes[0].MeshParts[0].Effect = aryEffects[EFFECT_LIGHT];
                            aryModels[MODEL_SPHERE].Meshes[0].Draw();
                        }
                    }
                    if (showConstruction)
                    {
                        // Draw structures - NO CULLING
                        RasterizerState oldCull = mapDevice.RasterizerState;
                        mapDevice.RasterizerState = stateNoCull;
                        // Draw
                        if (lstSectors[i].Vertices.Count > 0)
                        {
                            try
                            {
                                lstSectors[i].SectorEffect.CurrentTechnique.Passes[0].Apply();
                                mapDevice.DrawUserIndexedPrimitives<CustomVD>(PrimitiveType.TriangleList,
                                    lstSectors[i].Vertices.ToArray(), 0, lstSectors[i].Vertices.Count, lstSectors[i].Indices.ToArray(),
                                    0, (int)(lstSectors[i].Indices.Count / 3));
                            }
                            catch
                            { }
                        }
                        // Restore state
                        mapDevice.RasterizerState = oldCull;
                    }

                    // Draw Locations
                    if (showLocation)
                    {
                        // Draw Locations
                        for (int a = 0; a < lstSectors[i].Locations.Count; a++)
                        {
                            LocationBox boxHold = lstSectors[i].Locations[a] as LocationBox;
                            if (boxHold != null)
                            {
                                aryEffects[EFFECT_LOCATION].Parameters["world"].SetValue(
                                    Matrix.CreateScale(boxHold.WRadius, boxHold.HRadius, boxHold.DRadius)
                                    * Matrix.CreateWorld(boxHold.Position, boxHold.Forward, boxHold.Up));

                                aryModels[MODEL_CUBE].Meshes[0].MeshParts[0].Effect = aryEffects[EFFECT_LOCATION];
                                aryModels[MODEL_CUBE].Meshes[0].Draw();
                            }
                            else
                            {
                                LocationSphere sphereHold = lstSectors[i].Locations[a] as LocationSphere;
                                if (sphereHold != null)
                                {
                                    aryEffects[EFFECT_LOCATION].Parameters["world"].SetValue(Matrix.CreateScale(sphereHold.Radius)
                                    * Matrix.CreateTranslation(sphereHold.Position));

                                    aryModels[MODEL_SPHERE].Meshes[0].MeshParts[0].Effect = aryEffects[EFFECT_LOCATION];
                                    aryModels[MODEL_SPHERE].Meshes[0].Draw();
                                }
                            }
                        }

                        // Draw Relays
                        Relay[] aryRelays = lstSectors[i].Relays.ToArray();
                        for (int a = 0; a < aryRelays.Length; a++)
                        {
                            aryEffects[EFFECT_RELAY].Parameters["world"].SetValue(Matrix.CreateScale(1) * Matrix.CreateTranslation(aryRelays[a].Position));
                            aryModels[MODEL_SPHERE].Meshes[0].MeshParts[0].Effect = aryEffects[EFFECT_RELAY];
                            aryModels[MODEL_SPHERE].Meshes[0].Draw();
                            // Draw Links
                            Relay[] aryLinkedRelays = aryRelays[a].Relays;
                            for (int b = 0; b < aryLinkedRelays.Length; b++)
                            {
                                aryEffects[EFFECT_RELAY].Parameters["world"].SetValue(
                                    Matrix.CreateScale(.25f, .25f, .5f * Vector3.Distance(aryRelays[a].Position, aryLinkedRelays[b].Position))
                                    * Matrix.CreateWorld(Vector3.Lerp(aryRelays[a].Position, aryLinkedRelays[b].Position, .5f),
                                    Vector3.Normalize(aryRelays[a].Position - aryLinkedRelays[b].Position), Vector3.Up));

                                aryModels[MODEL_CUBE].Meshes[0].MeshParts[0].Effect = aryEffects[EFFECT_RELAY];
                                aryModels[MODEL_CUBE].Meshes[0].Draw();
                            }
                        }

                        // Draw Spawns
                        for (int a = 0; a < lstSectors[i].Spawns.Count; a++)
                        {
                            aryEffects[EFFECT_SPAWN].Parameters["world"].SetValue(Matrix.CreateScale(2) * Matrix.CreateTranslation(lstSectors[i].Spawns[a].Position));
                            aryModels[MODEL_SPHERE].Meshes[0].MeshParts[0].Effect = aryEffects[EFFECT_SPAWN];
                            aryModels[MODEL_SPHERE].Meshes[0].Draw();
                        }
                    } // if locations

                    // ******* DRAW WITH WIREFRAME *******
                    RasterizerState oldRast = drawDevice.RasterizerState;
                    drawDevice.RasterizerState = stateWireframe;

                    // Draw Bounds
                    if (showBounds)
                    {
                        for (int a = 0; a < lstSectors[i].Bounds.Count; a++)
                        {
                            BoundBox boxHold = lstSectors[i].Bounds[a] as BoundBox;
                            if (boxHold != null)
                            {
                                aryEffects[EFFECT_BOUND].Parameters["world"].SetValue(
                                    Matrix.CreateScale(boxHold.WRadius, boxHold.HRadius, boxHold.DRadius)
                                    * Matrix.CreateWorld(boxHold.Position, boxHold.Forward, boxHold.Up));

                                aryModels[MODEL_CUBE].Meshes[0].MeshParts[0].Effect = aryEffects[EFFECT_BOUND];
                                aryModels[MODEL_CUBE].Meshes[0].Draw();
                            }
                            else
                            {
                                BoundSphere sphereHold = lstSectors[i].Bounds[a] as BoundSphere;
                                if (sphereHold != null)
                                {
                                    aryEffects[EFFECT_BOUND].Parameters["world"].SetValue(Matrix.CreateScale(sphereHold.Radius)
                                    * Matrix.CreateTranslation(sphereHold.Position));

                                    aryModels[MODEL_SPHERE].Meshes[0].MeshParts[0].Effect = aryEffects[EFFECT_BOUND];
                                    aryModels[MODEL_SPHERE].Meshes[0].Draw();
                                }
                            }
                        }
                    } // if bounds

                    // Reset states
                    drawDevice.RasterizerState = oldRast;
                }
            }
        }

        public bool Intersects(BoundingSphere searchSphere)
        {
            bool intersecting = false;

            for (int i = 0; i < lstSectors.Count; i++)
            {
                for (int a = 0; a < lstSectors[i].Bounds.Count; a++)
                {
                    BoundingSphere testSphere = searchSphere;

                    BoundBox boxHold = lstSectors[i].Bounds[a] as BoundBox;
                    if (boxHold != null)
                    {
                        // Orient sphere
                        Matrix wallMat = Matrix.CreateWorld(boxHold.Position, boxHold.Forward, boxHold.Up);
                        testSphere.Center = Vector3.Transform(testSphere.Center, Matrix.Invert(wallMat));

                        if (testSphere.Intersects((BoundingBox)boxHold.CollisionBound))
                        { intersecting = true; }
                    }
                    // if sphere
                    else
                    {
                        BoundSphere sphereHold = lstSectors[i].Bounds[a] as BoundSphere;
                        if (sphereHold != null)
                        {
                            if (testSphere.Intersects((BoundingSphere)sphereHold.CollisionBound))
                            { intersecting = true; }
                        }
                    }
                }
            }
            return intersecting;
        }

        public void LoadMap(MapData newData, Effect newSectorEffect)
        {
            lstSectors = new List<Sector>(newData.arySectorData.Length);
            
            // Create Sectors
            for (int i = 0; i < newData.arySectorData.Length; i++)
            {
                Sector newSector = new Sector(newSectorEffect, newData.arySectorData[i].TextureIndex);
                FlagList<CustomVD> newVerts = new FlagList<CustomVD>(newData.arySectorData[i].Vertices);
                List<Int16> newInds = new List<Int16>(newData.arySectorData[i].Indices);
                // Verts / Inds
                newSector.Vertices = newVerts;
                newSector.Indices = newInds;
                // Complete sector
                lstSectors.Add(newSector);
            }

            // Pass through each new sector, add objects
            for (int i = 0; i < lstSectors.Count; i++)
            {
                // Lights
                for (int a = 0; a < newData.arySectorData[i].Lights.Length; a++)
                {
                    LightData lightData = newData.arySectorData[i].Lights[a];
                    (lstSectors[i].AddLight(lightData.Position, lightData.Forward, lightData.Up, lightData.Color, lightData.Intensity, lightData.Range)).OverrideID(lightData.ID);
                }

                // Locations
                for (int a = 0; a < newData.arySectorData[i].Locations.Length; a++)
                {
                    LocationData locData = newData.arySectorData[i].Locations[a];
                    if ((locData.DimensionH < .25f) && (locData.DimensionD < .25f))
                    { lstSectors[i].AddLocation(locData.Position, locData.DimensionRoW).OverrideID(locData.ID); }
                    else
                    { lstSectors[i].AddLocation(locData.Position, locData.Forward, locData.Up, locData.DimensionRoW, locData.DimensionH, locData.DimensionD).OverrideID(locData.ID); }
                }

                // Relays
                if (newData.arySectorData[i].Relays != null)
                {
                    for (int a = 0; a < newData.arySectorData[i].Relays.Length; a++)
                    {
                        RelayData newRelayData = newData.arySectorData[i].Relays[a];
                        lstSectors[i].AddRelay(newRelayData.Position).OverrideID(newRelayData.ID);
                    }
                }

                // Spawns
                for (int a = 0; a < newData.arySectorData[i].Spawns.Length; a++)
                {
                    SpawnData newSpawnData = newData.arySectorData[i].Spawns[a];
                    lstSectors[i].AddSpawn(newSpawnData.Position, newSpawnData.SpawnID, newSpawnData.Count, newSpawnData.Delay).OverrideID(newSpawnData.ID);
                }

                // Bounds
                for (int a = 0; a < newData.arySectorData[i].Bounds.Length; a++)
                {
                    BoundData boundData = newData.arySectorData[i].Bounds[a];
                    if (boundData.aryDimensions.Length == 1)
                    { lstSectors[i].AddBound(boundData.Position, boundData.aryDimensions[0]).OverrideID(boundData.ID); }
                    else if (boundData.aryDimensions.Length == 3)
                    {
                        lstSectors[i].AddBound(boundData.Position, boundData.Forward, boundData.Up,
                            boundData.aryDimensions[0], boundData.aryDimensions[1], boundData.aryDimensions[2]).OverrideID(boundData.ID);
                    }
                }

                // Structures and interactives - added manually
                for (int a = 0; a < newData.arySectorData[i].Structures.Length; a++)
                {
                    StructureData structData = newData.arySectorData[i].Structures[a];
                    if (structData.IsInteractive)
                    {
                        InteractiveStructure structHold = new InteractiveStructure(structData.Position, structData.Forward, structData.Up, structData.WRadius,
                            structData.HRadius, structData.DRadius, structData.StartVertex, structData.VertexCount, structData.StartIndex,
                            structData.IndexCount, structData.TexStart, structData.TexDimension, structData.BoundType);
                        lstSectors[i].Interactives.Add(structHold);
                        lstSectors[i].Entities.Add(structHold);
                        structHold.OverrideID(structData.ID);
                    }
                    // else static
                    else
                    {
                        StaticStructure structHold = new StaticStructure(structData.Position, structData.Forward, structData.Up, structData.WRadius,
                            structData.HRadius, structData.DRadius, structData.StartVertex, structData.VertexCount, structData.StartIndex,
                            structData.IndexCount, structData.TexStart, structData.TexDimension);
                        lstSectors[i].Structures.Add(structHold);
                        lstSectors[i].Entities.Add(structHold);
                        structHold.OverrideID(structData.ID);
                    }
                }

                // Pinned
                for (int a = 0; a < newData.arySectorData[i].PinnedStructures.Length; a++)
                {
                    PinnedData pinData = newData.arySectorData[i].PinnedStructures[a];
                    List<MapEntity> newPinned = new List<MapEntity>(pinData.PinnedObjects.Length);
                    for (int b = 0; b < pinData.PinnedObjects.Length; b++)
                    {
                        MapEntity newObject = lstSectors[i].IdentifyEntity(pinData.PinnedObjects[b]);
                        if (newObject != null)
                        { newPinned.Add(newObject); }
                        else { System.Windows.Forms.MessageBox.Show("Pinned Object not found during load."); }
                    }
                    (lstSectors[i].PinObjects(newPinned.ToArray())).OverrideID(pinData.ID);
                }

                // Animations
                for (int a = 0; a < newData.arySectorData[i].Animations.Length; a++)
                {
                    AnimationData animData = newData.arySectorData[i].Animations[a];
                    Animation newAnimation = new Animation(animData.Duration, animData.Speed, (PlayMode)animData.PlayMode, lstSectors[i].FlagAnimation);
                    newAnimation.Repeat = animData.Repeat;
                    newAnimation.OverrideID(animData.ID);
                    
                    List<MapEntity> animatedObjects = new List<MapEntity>(animData.SelectedObjects.Length);
                    AnimationFrame[] newFrames = new AnimationFrame[animData.aryFrames.Length];

                    // Animated objects
                    for (int b = 0; b < animData.SelectedObjects.Length; b++)
                    {
                        MapEntity newObject = lstSectors[i].IdentifyEntity(animData.SelectedObjects[b]);
                        if (newObject != null)
                        { newAnimation.AddObject(newObject); }
                        else { System.Windows.Forms.MessageBox.Show("Animated Object not found during load."); }
                    }

                    // Frames and states
                    for (int b = 0; b < newFrames.Length; b++)
                    {
                        newFrames[b] = new AnimationFrame(animData.aryFrames[b].TimePosition);
                        newFrames[b].OverrideID(animData.aryFrames[b].ID);
                        for (int c = 0; c < animData.aryFrames[b].aryStates.Length; c++)
                        {
                            StateData stateData = animData.aryFrames[b].aryStates[c];
                            newFrames[b].States.Add(new ObjectState(stateData.Position, stateData.Forward, stateData.Up));
                        }
                    }
                    newAnimation.Frames = newFrames;
                    lstSectors[i].Animations.Add(newAnimation);
                    lstSectors[i].Entities.Add(newAnimation);
                }
                // Triggers
                for (int a = 0; a < newData.arySectorData[i].Triggers.Length; a++)
                {
                    TriggerData trigData = newData.arySectorData[i].Triggers[a];
                    MapEntity targetObject = lstSectors[i].IdentifyEntity(trigData.TargetObject);
                    if (trigData.CommandIndex != -1)
                    { (lstSectors[i].AddCommandTrigger(targetObject, trigData.CommandIndex, trigData.Occurrences)).OverrideID(trigData.ID); }
                    else if (trigData.LocationIndex != -1)
                    {
                        MapEntity targetLoc = lstSectors[i].IdentifyEntity(trigData.LocationIndex);
                        if (targetLoc is Location)
                        { (lstSectors[i].AddProximityTrigger(targetObject, (Location)targetLoc, trigData.Occurrences)).OverrideID(trigData.ID); }
                        else
                        { System.Windows.Forms.MessageBox.Show("No Location for Proximity Trigger found during load."); }
                    }
                    else if (trigData.Delay != -1)
                    { (lstSectors[i].AddTimedTrigger(targetObject, trigData.Delay, trigData.Occurrences)).OverrideID(trigData.ID); }
                }

                // Post Processing

                // Link Relays
                if (newData.arySectorData[i].Relays != null)
                {
                    for (int a = 0; a < newData.arySectorData[i].Relays.Length; a++)
                    {
                        Relay relayHold = lstSectors[i].IdentifyEntity(newData.arySectorData[i].Relays[a].ID) as Relay;
                        RelayData newRelayData = newData.arySectorData[i].Relays[a];
                        for (int b = 0; b < newRelayData.aryLinked.Length; b++)
                        { lstSectors[i].LinkObjects(relayHold, lstSectors[i].IdentifyEntity(newRelayData.aryLinked[b])); }
                    }
                }
                lstSectors[i].CreateCollisionSphere();

                // Quick Fix - Offset to new IDs
                int largest = 0;
                for (int a = 0; a < lstSectors[i].Entities.Count; a++)
                {
                    if (lstSectors[i].Entities[a].ID > largest)
                    { largest = lstSectors[i].Entities[a].ID; }
                }
                MapEntity entityWaste = new MapEntity();
                while (entityWaste.ID <= largest)
                {
                    entityWaste = new MapEntity();
                }
            }
        }

        public MapData SaveMap()
        {
            MapData saveData = new MapData();
            SectorData[] arySectorData = new SectorData[lstSectors.Count];

            // Create Sector data
            for (int i = 0; i < arySectorData.Length; i++)
            {
                Sector sectorHold = lstSectors[i];                
                AnimationData[] newAnimationData = new AnimationData[sectorHold.Animations.Count];
                TriggerData[] newTriggerData = new TriggerData[sectorHold.Triggers.Count];
                LightData[] newLights = new LightData[sectorHold.Lights.Count];
                LocationData[] newLocations = new LocationData[sectorHold.Locations.Count];
                RelayData[] newRelays = new RelayData[sectorHold.Relays.Count];
                SpawnData[] newSpawns = new SpawnData[sectorHold.Spawns.Count];
                BoundData[] newBounds = new BoundData[sectorHold.Bounds.Count];
                StructureData[] newStructureData = new StructureData[sectorHold.Structures.Count + sectorHold.Interactives.Count];
                PinnedData[] newPinnedData = new PinnedData[sectorHold.PinnedStructures.Count];

                // Animations 
                for (int a = 0; a < lstSectors[i].Animations.Count; a++)
                {
                    Animation animationHold = lstSectors[i].Animations[a];
                    MapEntity[] animatedObjects = animationHold.AnimatedObjects;
                    int[] animatedIndices = new int[animatedObjects.Length];
                    AnimationFrame[] currentFrames = animationHold.Frames;
                    FrameData[] newFrames = new FrameData[currentFrames.Length];

                    // Get object IDs
                    for (int b = 0; b < animatedObjects.Length; b++)
                    { animatedIndices[b] = animatedObjects[b].ID; }

                    // Frames 
                    for (int b = 0; b < currentFrames.Length; b++)
                    {
                        StateData[] newStates = new StateData[animatedObjects.Length];

                        // States 
                        for (int c = 0; c < newStates.Length; c++)
                        { newStates[c] = new StateData(currentFrames[b].States[c].Position, currentFrames[b].States[c].Forward, currentFrames[b].States[c].Up); }
                        // Finalize frame data 
                        newFrames[b] = new FrameData(currentFrames[b].ID, currentFrames[b].TimePosition, newStates);
                    }
                    // Finalize animation data 
                    newAnimationData[a] = new AnimationData(animationHold.ID, (int)animationHold.PlayMode, animationHold.Duration,
                        animationHold.Speed, animationHold.Repeat, animatedIndices, newFrames);
                }

                // Triggers
                for (int a = 0; a < newTriggerData.Length; a++)
                {
                    Trigger triggerHold = sectorHold.Triggers[a];
                    int newTargetIndex = -1;
                    int newCommandIndex = -1;
                    int newLocationIndex = -1;
                    float newDelay = -1;

                    // Target
                    if (triggerHold.TargetObject != null)
                    { newTargetIndex = triggerHold.TargetObject.ID; }

                    // Trigger specific
                    if (triggerHold is CommandTrigger)
                    { newCommandIndex = ((CommandTrigger)triggerHold).CommandIndex; }
                    else if (triggerHold is ProximityTrigger)
                    { newLocationIndex = ((ProximityTrigger)triggerHold).Location.ID; }
                    else if (triggerHold is TimedTrigger)
                    { newDelay = ((TimedTrigger)triggerHold).Delay; }

                    newTriggerData[a] = new TriggerData(triggerHold.ID, newTargetIndex, triggerHold.Occurences, newCommandIndex,
                        newLocationIndex, newDelay);
                }

                // Lights
                for (int a = 0; a < newLights.Length; a++)
                {
                    newLights[a] = new LightData(sectorHold.Lights[a].ID, sectorHold.Lights[a].Position, sectorHold.Lights[a].Forward, sectorHold.Lights[a].Up,
                        sectorHold.Lights[a].Color, sectorHold.Lights[a].Intensity, sectorHold.Lights[a].Range);
                }

                // Locations
                for (int a = 0; a < newLocations.Length; a++)
                {
                    LocationBox locBox = sectorHold.Locations[a] as LocationBox;
                    if (locBox != null)
                    { newLocations[a] = new LocationData(locBox.ID, locBox.Position, locBox.Forward, locBox.Up, locBox.WRadius, locBox.HRadius, locBox.DRadius); }
                    else
                    { newLocations[a] = new LocationData(sectorHold.Locations[a].ID, sectorHold.Locations[a].Position, ((LocationSphere)sectorHold.Locations[a]).Radius); }
                }

                // Relays
                for (int a = 0; a < newRelays.Length; a++)
                {
                    Relay[] linkedRelays = sectorHold.Relays[a].Relays;
                    int[] newLinked = new int[linkedRelays.Length];
                    for (int b = 0; b < linkedRelays.Length; b++)
                    { newLinked[b] = linkedRelays[b].ID; }
                    newRelays[a] = new RelayData(sectorHold.Relays[a].ID, sectorHold.Relays[a].Position, newLinked);
                }

                // Spawns
                for (int a = 0; a < newSpawns.Length; a++)
                {
                    newSpawns[a] = new SpawnData(sectorHold.Spawns[a].ID, sectorHold.Spawns[a].SpawnID, sectorHold.Spawns[a].Position,
                        sectorHold.Spawns[a].Count, sectorHold.Spawns[a].Delay);
                }

                // Bounds
                for (int a = 0; a < newBounds.Length; a++)
                {
                    BoundBox boundBox = sectorHold.Bounds[a] as BoundBox;
                    if (boundBox != null)
                    { newBounds[a] = new BoundData(boundBox.ID, boundBox.Position, boundBox.Forward, boundBox.Up, boundBox.IsLadder, boundBox.WRadius, boundBox.HRadius, boundBox.DRadius); }
                    else
                    { newBounds[a] = new BoundData(sectorHold.Bounds[a].ID, sectorHold.Bounds[a].Position, sectorHold.Bounds[a].IsLadder, ((BoundSphere)sectorHold.Bounds[a]).Radius); }
                }

                // Structures
                for (int a = 0; a < sectorHold.Structures.Count; a++)
                {
                    StaticStructure structHold = sectorHold.Structures[a];
                    // Create structure data
                    newStructureData[a] = new StructureData(structHold.ID, structHold.Position, structHold.Forward, structHold.Up, structHold.WRadius,
                        structHold.HRadius, structHold.DRadius, structHold.StartVertex, structHold.VertexCount, structHold.StartIndex,
                        structHold.IndexCount, structHold.TexStart, structHold.TexDimension, BoundType.Null, false);
                }

                // Interactive Structures placed after structures in same data
                for (int a = 0; a < sectorHold.Interactives.Count; a++)
                {
                    // Reset for saving
                    InteractiveStructure structHold = sectorHold.Interactives[a];
                    sectorHold.ResetObject(structHold);
                    // Create structure data
                    newStructureData[sectorHold.Structures.Count + a] = new StructureData(structHold.ID, structHold.Position, structHold.Forward, structHold.Up, structHold.WRadius,
                        structHold.HRadius, structHold.DRadius, structHold.StartVertex, structHold.VertexCount, structHold.StartIndex,
                        structHold.IndexCount, structHold.TexStart, structHold.TexDimension, structHold.BoundType, true);
                }

                // Pinned
                for (int a = 0; a < newPinnedData.Length; a++)
                {
                    PinnedStructure pinHold = sectorHold.PinnedStructures[a];
                    MapEntity[] pinnedObjects = pinHold.Pinned;
                    int[] pinnedIndices = new int[pinnedObjects.Length];

                    for (int b = 0; b < pinnedObjects.Length; b++)
                    { pinnedIndices[b] = pinnedObjects[b].ID; }

                    newPinnedData[a] = new PinnedData(pinHold.ID, pinHold.Position, pinHold.Forward, pinHold.Up, pinnedIndices);
                }

                // Create sector data
                arySectorData[i] = new SectorData(sectorHold.Vertices.ToArray(), sectorHold.Indices.ToArray(), null, null, lstSectors[i].TextureIndex,
                    newAnimationData, newTriggerData, newLights, newLocations, newRelays, newSpawns, newBounds, newStructureData, newPinnedData);
            }

            // Set sectors
            saveData.arySectorData = arySectorData;
            return saveData;
        }

        public MapData ExportMap()
        {
            MapData saveData = new MapData();
            SectorData[] arySectorData = new SectorData[lstSectors.Count];

            // Create Sector data
            for (int i = 0; i < arySectorData.Length; i++)
            {
                Sector sectorHold = lstSectors[i];

                List<CustomVD> lstVerts = new List<CustomVD>(sectorHold.Vertices.Count);
                List<Int16> lstInds = new List<Int16>(sectorHold.Indices.Count);

                List<CustomVD> lstDVerts = new List<CustomVD>(sectorHold.Vertices.Count);
                List<Int16> lstDInds = new List<Int16>(sectorHold.Indices.Count);

                AnimationData[] newAnimationData = new AnimationData[sectorHold.Animations.Count];
                TriggerData[] newTriggerData = new TriggerData[sectorHold.Triggers.Count];
                LightData[] newLights = new LightData[sectorHold.Lights.Count];
                LocationData[] newLocations = new LocationData[sectorHold.Locations.Count];
                RelayData[] newRelays = new RelayData[sectorHold.Relays.Count];
                SpawnData[] newSpawns = new SpawnData[sectorHold.Spawns.Count];
                BoundData[] newBounds = new BoundData[sectorHold.Bounds.Count];
                StructureData[] newStructureData = new StructureData[sectorHold.Structures.Count + sectorHold.Interactives.Count];
                PinnedData[] newPinnedData = new PinnedData[sectorHold.PinnedStructures.Count];

                // Animations 
                for (int a = 0; a < lstSectors[i].Animations.Count; a++)
                {
                    Animation animationHold = lstSectors[i].Animations[a];
                    MapEntity[] animatedObjects = animationHold.AnimatedObjects;
                    int[] animatedIndices = new int[animatedObjects.Length];
                    AnimationFrame[] currentFrames = animationHold.Frames;
                    FrameData[] newFrames = new FrameData[currentFrames.Length];

                    // Get object IDs
                    for (int b = 0; b < animatedObjects.Length; b++)
                    { animatedIndices[b] = animatedObjects[b].ID; }

                    // Frames 
                    for (int b = 0; b < currentFrames.Length; b++)
                    {
                        StateData[] newStates = new StateData[animatedObjects.Length];

                        // States 
                        for (int c = 0; c < newStates.Length; c++)
                        { newStates[c] = new StateData(currentFrames[b].States[c].Position, currentFrames[b].States[c].Forward, currentFrames[b].States[c].Up); }
                        // Finalize frame data 
                        newFrames[b] = new FrameData(currentFrames[b].ID, currentFrames[b].TimePosition, newStates);
                    }
                    // Finalize animation data 
                    newAnimationData[a] = new AnimationData(animationHold.ID, (int)animationHold.PlayMode, animationHold.Duration,
                        animationHold.Speed, animationHold.Repeat, animatedIndices, newFrames);
                }

                // Triggers
                for (int a = 0; a < newTriggerData.Length; a++)
                {
                    Trigger triggerHold = sectorHold.Triggers[a];
                    int newTargetIndex = -1;
                    int newCommandIndex = -1;
                    int newLocationIndex = -1;
                    float newDelay = -1;

                    // Target
                    if (triggerHold.TargetObject != null)
                    { newTargetIndex = triggerHold.TargetObject.ID; }

                    // Trigger specific
                    if (triggerHold is CommandTrigger)
                    { newCommandIndex = ((CommandTrigger)triggerHold).CommandIndex; }
                    else if (triggerHold is ProximityTrigger)
                    { newLocationIndex = ((ProximityTrigger)triggerHold).Location.ID; }
                    else if (triggerHold is TimedTrigger)
                    { newDelay = ((TimedTrigger)triggerHold).Delay; }

                    newTriggerData[a] = new TriggerData(triggerHold.ID, newTargetIndex, triggerHold.Occurences, newCommandIndex,
                        newLocationIndex, newDelay);
                }

                // Lights
                for (int a = 0; a < newLights.Length; a++)
                {
                    newLights[a] = new LightData(sectorHold.Lights[a].ID, sectorHold.Lights[a].Position, sectorHold.Lights[a].Forward, sectorHold.Lights[a].Up,
                        sectorHold.Lights[a].Color, sectorHold.Lights[a].Intensity, sectorHold.Lights[a].Range);
                }

                // Locations
                for (int a = 0; a < newLocations.Length; a++)
                {
                    LocationBox locBox = sectorHold.Locations[a] as LocationBox;
                    if (locBox != null)
                    { newLocations[a] = new LocationData(locBox.ID, locBox.Position, locBox.Forward, locBox.Up, locBox.WRadius, locBox.HRadius, locBox.DRadius); }
                    else
                    { newLocations[a] = new LocationData(sectorHold.Locations[a].ID, sectorHold.Locations[a].Position, ((LocationSphere)sectorHold.Locations[a]).Radius); }
                }

                // Relays
                for (int a = 0; a < newRelays.Length; a++)
                {
                    Relay[] linkedRelays = sectorHold.Relays[a].Relays;
                    int[] newLinked = new int[linkedRelays.Length];
                    for (int b = 0; b < linkedRelays.Length; b++)
                    { newLinked[b] = linkedRelays[b].ID; }
                    newRelays[a] = new RelayData(sectorHold.Relays[a].ID, sectorHold.Relays[a].Position, newLinked);
                }

                // Spawns
                for (int a = 0; a < newSpawns.Length; a++)
                {
                    newSpawns[a] = new SpawnData(sectorHold.Spawns[a].ID, sectorHold.Spawns[a].SpawnID, sectorHold.Spawns[a].Position,
                        sectorHold.Spawns[a].Count, sectorHold.Spawns[a].Delay);
                }

                // Bounds
                for (int a = 0; a < newBounds.Length; a++)
                {
                    BoundBox boundBox = sectorHold.Bounds[a] as BoundBox;
                    if (boundBox != null)
                    { newBounds[a] = new BoundData(boundBox.ID, boundBox.Position, boundBox.Forward, boundBox.Up, boundBox.IsLadder, boundBox.WRadius, boundBox.HRadius, boundBox.DRadius); }
                    else
                    { newBounds[a] = new BoundData(sectorHold.Bounds[a].ID, sectorHold.Bounds[a].Position, sectorHold.Bounds[a].IsLadder, ((BoundSphere)sectorHold.Bounds[a]).Radius); }
                }

                // Structures
                for (int a = 0; a < sectorHold.Structures.Count; a++)
                {
                    StaticStructure structHold = sectorHold.Structures[a];
                    int newStartVertex;
                    int newStartIndex;
                    // if dynamic buffer
                    if (structHold.IsAnimated)
                    {
                        newStartVertex = lstDVerts.Count;
                        // Vertices
                        for (int pV = structHold.StartVertex; pV < structHold.StartVertex + structHold.VertexCount; pV++)
                        { lstDVerts.Add(sectorHold.Vertices[pV]); }
                        // Indices
                        newStartIndex = lstDInds.Count;
                        for (int pI = structHold.StartIndex; pI < structHold.StartIndex + structHold.IndexCount; pI++)
                        { lstDInds.Add((Int16)(sectorHold.Indices[pI] + (newStartVertex - structHold.StartVertex))); }
                    }
                    // else static buffer
                    else
                    {
                        newStartVertex = lstVerts.Count;
                        // Vertices
                        for (int pV = structHold.StartVertex; pV < structHold.StartVertex + structHold.VertexCount; pV++)
                        { lstVerts.Add(sectorHold.Vertices[pV]); }
                        // Indices
                        newStartIndex = lstInds.Count;
                        for (int pI = structHold.StartIndex; pI < structHold.StartIndex + structHold.IndexCount; pI++)
                        { lstInds.Add((Int16)(sectorHold.Indices[pI] + (newStartVertex - structHold.StartVertex))); }
                    }
                    // Create structure data
                    newStructureData[a] = new StructureData(structHold.ID, structHold.Position, structHold.Forward, structHold.Up, structHold.WRadius,
                        structHold.HRadius, structHold.DRadius, newStartVertex, structHold.VertexCount, newStartIndex,
                        structHold.IndexCount, structHold.TexStart, structHold.TexDimension, BoundType.Null, false);
                }

                // Interactive Structures placed after structures in same data
                for (int a = 0; a < sectorHold.Interactives.Count; a++)
                {
                    // Reset for saving
                    InteractiveStructure structHold = sectorHold.Interactives[a];
                    sectorHold.ResetObject(structHold);
                    // Create structure data
                    newStructureData[sectorHold.Structures.Count + a] = new StructureData(structHold.ID, structHold.Position, structHold.Forward, structHold.Up, structHold.WRadius,
                        structHold.HRadius, structHold.DRadius, structHold.StartVertex, structHold.VertexCount, structHold.StartIndex,
                        structHold.IndexCount, structHold.TexStart, structHold.TexDimension, structHold.BoundType, true);
                }

                // Pinned
                for (int a = 0; a < newPinnedData.Length; a++)
                {
                    PinnedStructure pinHold = sectorHold.PinnedStructures[a];
                    MapEntity[] pinnedObjects = pinHold.Pinned;
                    int[] pinnedIndices = new int[pinnedObjects.Length];

                    for (int b = 0; b < pinnedObjects.Length; b++)
                    { pinnedIndices[b] = pinnedObjects[b].ID; }

                    newPinnedData[a] = new PinnedData(pinHold.ID, pinHold.Position, pinHold.Forward, pinHold.Up, pinnedIndices);
                }

                // Create sector data
                arySectorData[i] = new SectorData(lstVerts.ToArray(), lstInds.ToArray(), lstDVerts.ToArray(), lstDInds.ToArray(), lstSectors[i].TextureIndex,
                    newAnimationData, newTriggerData, newLights, newLocations, newRelays, newSpawns, newBounds, newStructureData, newPinnedData);
            }

            // Set sectors
            saveData.arySectorData = arySectorData;
            return saveData;
        }

        public void Unload()
        { mapManager.Unload(); }

        public void Update(float tSeconds)
        {
            for (int i = 0; i < lstSectors.Count; i++)
            { lstSectors[i].Update(tSeconds); }
        }

        // ****** SUB CLASSES ******

        public class Sector
        {
            private List<SectorFlag> lstFlags = new List<SectorFlag>(2);
            public FlagList<CustomVD> Vertices;
            public List<Int16> Indices;
            public Effect SectorEffect;
            public Location DrawLocation;
            private BoundingSphere bound;

            public int TextureIndex;
            public float Gravity = 60;
            public bool IsVisible = true;

            private List<MapEntity> lstEntities = new List<MapEntity>(300);
            private List<Animation> lstAnimations = new List<Animation>(10);
            private List<Trigger> lstTriggers = new List<Trigger>(1);
            private List<PinnedStructure> lstPinned = new List<PinnedStructure>(50);

            // Placable Objects
            private List<Bound> lstBounds = new List<Bound>(100);
            private List<InteractiveStructure> lstInteractives = new List<InteractiveStructure>(50);
            private List<Light> lstLights = new List<Light>(10);
            private List<Location> lstLocations = new List<Location>(10);
            private List<Relay> lstRelays = new List<Relay>();
            private List<Spawn> lstSpawns = new List<Spawn>(10);
            private List<StaticStructure> lstStructures = new List<StaticStructure>(100);

            // Specialty
            private List<MapEntity> lstCollidables = new List<MapEntity>();
            private List<MapEntity> lstUpdatables = new List<MapEntity>();

            public Sector(Effect newEffect, int newTextureIndex)
            {
                Vertices = new FlagList<CustomVD>(4800);
                Indices = new List<Int16>(7200);
                SectorEffect = newEffect;
                TextureIndex = newTextureIndex;

                // Links
                Vertices.FlagEvent += new ThrowFlag(FlagVertices);
            }

            // ****** ACCESSORS ******
            public BoundingSphere Bound { get { return Bound; } }
            public List<MapEntity> Entities { get { return lstEntities; } }
            public List<Animation> Animations { get { return lstAnimations; } }
            public List<Trigger> Triggers { get { return lstTriggers; } }
            public List<PinnedStructure> PinnedStructures { get { return lstPinned; } }

            public List<Bound> Bounds { get { return lstBounds; } }
            public List<InteractiveStructure> Interactives { get { return lstInteractives; } }
            public List<Light> Lights { get { return lstLights; } }
            public List<Location> Locations { get { return lstLocations; } }
            public List<Relay> Relays { get { return lstRelays; } }
            public List<Spawn> Spawns { get { return lstSpawns; } }
            public List<StaticStructure> Structures { get { return lstStructures; } }

            // ****** PRIVATE METHODS ******

            private void ApplyState(MapEntity fromSelectable, ObjectState newState)
            {
                Matrix oldWorld = Matrix.Identity;
                Matrix newWorld = Matrix.CreateWorld(newState.Position, newState.Forward, newState.Up);

                // Update object
                if (fromSelectable is IObject)
                {
                    ObjectState oldState = new ObjectState(((IObject)fromSelectable).Position, Vector3.Forward, Vector3.Up);
                    ((IObject)fromSelectable).Position = newState.Position;
                    if (fromSelectable is IOriented)
                    {
                        oldState.Forward = ((IOriented)fromSelectable).Forward;
                        oldState.Up = ((IOriented)fromSelectable).Up;
                        ((IOriented)fromSelectable).Forward = newState.Forward;
                        ((IOriented)fromSelectable).Up = newState.Up;
                    }
                    oldWorld = Matrix.CreateWorld(oldState.Position, oldState.Forward, oldState.Up);
                }

                // Update vertexed objects
                if (fromSelectable is IVertexed)
                {
                    // Update vertices
                    for (int a = ((IVertexed)fromSelectable).StartVertex; a < ((IVertexed)fromSelectable).StartVertex + ((IVertexed)fromSelectable).VertexCount; a++)
                    {
                        CustomVD vertPass = Vertices[a];
                        vertPass.Position = Vector3.Transform(vertPass.Position, Matrix.Invert(oldWorld) * newWorld);
                        vertPass.Normal = Vector3.TransformNormal(vertPass.Normal, Matrix.Invert(oldWorld) * newWorld);
                        Vertices[a] = vertPass;
                    }
                }

                // Recurse any pinnables objects
                if (fromSelectable is IPin)
                {
                    MapEntity[] pinned = ((IPin)fromSelectable).Pinned;
                    Vector3 dPos = Vector3.Zero;
                    if (fromSelectable is IObject) { dPos = ((IObject)fromSelectable).Position; }
                    for (int a = 0; a < pinned.Length; a++)
                    {
                        if (pinned[a] is IObject)
                        { newState.Position += ((IObject)pinned[a]).Position - dPos; }
                        ApplyState(pinned[a], newState);
                    }
                }
            }

            private void ModifyTexCoords(PalettedStructure modStruct, Vector2 newTexStart, Vector2 newTexDimension)
            {
                for (int i = modStruct.StartVertex; i < modStruct.StartVertex + modStruct.VertexCount; i++)
                {
                    CustomVD vertPass = Vertices[i];
                    Vector2 newTexCoord = vertPass.TexCoord;

                    // Reset texture coordinates
                    newTexCoord.X = (newTexCoord.X - modStruct.TexStart.X) / modStruct.TexDimension.X;
                    newTexCoord.Y = (newTexCoord.Y - modStruct.TexStart.Y) / modStruct.TexDimension.Y;

                    // Resize for new dimensions
                    newTexCoord.X = newTexStart.X + (newTexCoord.X * newTexDimension.X);
                    newTexCoord.Y = newTexStart.Y + (newTexCoord.Y * newTexDimension.Y);

                    // Update vertex
                    vertPass.TexCoord = newTexCoord;
                    Vertices[i] = vertPass;
                }
                // Update structure
                modStruct.TexStart = newTexStart;
                modStruct.TexDimension = newTexDimension;
            }

            private void RemoveStructure(PalettedStructure removeStruct)
            {
                int structIndex = -1;
                FlagList<CustomVD> lstVerts = new FlagList<CustomVD>(Vertices);
                List<Int16> lstInds = new List<Int16>(Indices);

                lstVerts.RemoveRange(removeStruct.StartVertex, removeStruct.VertexCount);
                lstInds.RemoveRange(removeStruct.StartIndex, removeStruct.IndexCount);

                // Adjust affected indices
                for (int i = removeStruct.StartIndex; i < lstInds.Count; i++)
                { lstInds[i] -= (Int16)removeStruct.VertexCount; }

                // Adjust affected structures
                for (int i = 0; i < Structures.Count; i++)
                {
                    PalettedStructure adjustStruct = Structures[i];
                    if (adjustStruct.StartVertex == removeStruct.StartVertex)
                    { structIndex = i; }
                    else if (adjustStruct.StartVertex > removeStruct.StartVertex)
                    {
                        adjustStruct.StartVertex -= removeStruct.VertexCount;
                        adjustStruct.StartIndex -= removeStruct.IndexCount;
                    }
                }

                // Update
                Structures.RemoveAt(structIndex);
                Vertices = lstVerts;
                Indices = lstInds;
            }

            private void RotateObject(MapEntity fromSelectable, Matrix rotMat, Matrix transMat)
            {
                if (fromSelectable is IObject)
                { ((IObject)fromSelectable).Position = Vector3.Transform(((IObject)fromSelectable).Position, Matrix.Invert(transMat) * rotMat * transMat); }

                if (fromSelectable is IOriented)
                {
                    ((IOriented)fromSelectable).Forward = Vector3.TransformNormal(((IOriented)fromSelectable).Forward, rotMat);
                    ((IOriented)fromSelectable).Up = Vector3.TransformNormal(((IOriented)fromSelectable).Up, rotMat);
                }

                if (fromSelectable is IVertexed)
                {
                    // Rotate vertices
                    for (int i = ((IVertexed)fromSelectable).StartVertex; i < ((IVertexed)fromSelectable).StartVertex + ((IVertexed)fromSelectable).VertexCount; i++)
                    {
                        CustomVD vertexPass = Vertices[i];
                        vertexPass.Position = Vector3.Transform(vertexPass.Position, Matrix.Invert(transMat) * rotMat * transMat);
                        vertexPass.Normal = Vector3.TransformNormal(vertexPass.Normal, rotMat);
                        Vertices[i] = vertexPass;
                    }
                }

                // Recurse any pinnables objects
                if (fromSelectable is IPin)
                {
                    MapEntity[] pinned = ((IPin)fromSelectable).Pinned;
                    for (int i = 0; i < pinned.Length; i++)
                    { RotateObject(pinned[i], rotMat, transMat); }
                }
            }

            // ***** USER METHODS *****

            public void ActivateObject(params MapEntity[] fromSelected) 
{ 
                for (int pS = 0; pS < fromSelected.Length; pS++) 
                { 
                    if ((fromSelected[pS] is IActivatable) && !((IActivatable)fromSelected[pS]).IsActive)
                    { 
                        ((IActivatable)fromSelected[pS]).Activate();

                        if (fromSelected[pS] is ICollidable)
                        { lstCollidables.Add(fromSelected[pS]); }

                        if (fromSelected[pS] is IUpdatable)
                        { lstUpdatables.Add(fromSelected[pS]); } 
                    } 
                } 
} 

            public MapEntity AddAnimation()
            {
                Animation newAnimation = new Animation(5, FlagAnimation);
                newAnimation.Deactivate();
                Animations.Add(newAnimation);
                Entities.Add(newAnimation);
                return Animations[Animations.Count - 1];
            }

            public MapEntity AddBound(Vector3 newPos, float newRadius)
            {
                BoundSphere newSphere = new BoundSphere(newPos, newRadius);
                Bounds.Add(newSphere);
                Entities.Add(newSphere);
                CreateCollisionSphere();
                return newSphere;
            }

            public MapEntity AddBound(Vector3 newPos, Vector3 newForward, Vector3 newUp, float newW, float newH, float newD)
            {
                BoundBox newBox = new BoundBox(newPos, newForward, newUp, newW, newH, newD);
                Entities.Add(newBox);
                Bounds.Add(newBox);
                CreateCollisionSphere();
                return newBox;
            }

            public MapEntity AddCommandTrigger(MapEntity newTarget, int newCommand, int newOccurrences)
            {
                CommandTrigger newTrigger = new CommandTrigger(newTarget, newCommand, newOccurrences);
                Triggers.Add(newTrigger);
                Entities.Add(newTrigger);
                return newTrigger;
            }

            public MapEntity AddInteractiveStructure(CustomVD[] aryVerts, Int16[] aryInds, Vector3 newPosition,
                Vector3 newForward, Vector3 newUp, Vector2 newTexStart, Vector2 newTexDimension, float newWRadius, float newHRadius, float newDRadius, BoundType newBoundType)
            {
                // Prepare new structure
                InteractiveStructure structHold = new InteractiveStructure(newPosition, newForward, newUp, newWRadius, newHRadius, newDRadius,
                    Vertices.Count, aryVerts.Length, Indices.Count, aryInds.Length, newTexStart, newTexDimension, newBoundType);

                // Add Vertices
                for (int i = 0; i < aryVerts.Length; i++)
                { Vertices.Add(aryVerts[i]); }

                // Add indices
                for (int i = 0; i < aryInds.Length; i++)
                { Indices.Add((Int16)(structHold.StartVertex + aryInds[i])); }

                // Finalize structure
                Entities.Add(structHold);
                Interactives.Add(structHold);
                // Return created structure
                return structHold;
            }

            public MapEntity AddStructure(CustomVD[] aryVerts, Int16[] aryInds, Vector3 newPosition,
            Vector3 newForward, Vector3 newUp, Vector2 newTexStart, Vector2 newTexDimension, float newWRadius, float newHRadius, float newDRadius)
            {
                Vector3 boxMin = new Vector3(-newWRadius, -newHRadius, -newDRadius);
                Vector3 boxMax = new Vector3(newWRadius, newHRadius, newDRadius);

                // Prepare new structure
                StaticStructure structHold = new StaticStructure(newPosition, newForward, newUp, newWRadius, newHRadius, newDRadius,
                    Vertices.Count, aryVerts.Length, Indices.Count, aryInds.Length, newTexStart, newTexDimension);

                // Add Vertices
                for (int i = 0; i < aryVerts.Length; i++)
                { Vertices.Add(aryVerts[i]); }

                // Add indices
                for (int i = 0; i < aryInds.Length; i++)
                { Indices.Add((Int16)(structHold.StartVertex + aryInds[i])); }

                // Finalize structure
                Entities.Add(structHold);
                Structures.Add(structHold);
                // Return created structure
                return structHold;
            }

            public MapEntity AddLight(Vector3 newPosition, Vector3 newForward, Vector3 newUp, Color newColor, float newIntesity, float newRange)
            {
                Light newLight = new Light(newPosition, newForward, newUp, newColor, newIntesity, newRange);
                Entities.Add(newLight);
                Lights.Add(newLight);
                return newLight;
            }

            public MapEntity AddLocation(Vector3 newPos, float newRadius)
            {
                LocationSphere newLocation = new LocationSphere(newPos, newRadius);
                Entities.Add(newLocation);
                Locations.Add(newLocation);
                return newLocation;
            }

            public MapEntity AddLocation(Vector3 newPos, Vector3 newForward, Vector3 newUp, float newW, float newH, float newD)
            {
                LocationBox newLocation = new LocationBox(newPos, newForward, newUp, newW, newH, newD);
                Entities.Add(newLocation);
                Locations.Add(newLocation);
                return newLocation;
            }

            // Only copies from selectables (visual objects)
            public MapEntity[] AddObject(params MapEntity[] fromSelected)
            {
                List<MapEntity> returnSelected = new List<MapEntity>(fromSelected.Length);
                for (int pS = 0; pS < fromSelected.Length; pS++)
                {
                    switch (fromSelected[pS].Type)
                    {
                        case ObjectType.Bound:
                            BoundBox selectedBoundBox = fromSelected[pS] as BoundBox;
                            if (selectedBoundBox != null)
                            {
                                returnSelected.Add(AddBound(selectedBoundBox.Position, selectedBoundBox.Forward,
                                    selectedBoundBox.Up, selectedBoundBox.WRadius, selectedBoundBox.HRadius, selectedBoundBox.DRadius));
                            }
                            else
                            {
                                BoundSphere selectedBoundSphere = fromSelected[pS] as BoundSphere;
                                returnSelected.Add(AddBound(selectedBoundSphere.Position, selectedBoundSphere.Radius));
                            }
                            break;
                        case ObjectType.Light:
                            Light selectedLight = fromSelected[pS] as Light;
                            returnSelected.Add(AddLight(selectedLight.Position, selectedLight.Forward, selectedLight.Up, selectedLight.Color,
                                selectedLight.Intensity, selectedLight.Range));
                            break;
                        case ObjectType.Location:
                            LocationBox selectedBox = fromSelected[pS] as LocationBox;
                            if (selectedBox != null)
                            {
                                returnSelected.Add(AddLocation(selectedBox.Position, selectedBox.Forward,
                                    selectedBox.Up, selectedBox.WRadius, selectedBox.HRadius, selectedBox.DRadius));
                            }
                            else
                            {
                                LocationSphere selectedSphere = fromSelected[pS] as LocationSphere;
                                returnSelected.Add(AddLocation(selectedSphere.Position, selectedSphere.Radius));
                            }
                            break;
                        case ObjectType.Pinned:
                            PinnedStructure selectedPin = fromSelected[pS] as PinnedStructure;
                            List<MapEntity> newPinned = new List<MapEntity>(selectedPin.Pinned.Length);

                            // Create copies of pinned
                            for (int i = 0; i < selectedPin.Pinned.Length; i++)
                            { newPinned.Add(AddObject(selectedPin.Pinned[i])[0]); }

                            returnSelected.Add(PinObjects(newPinned.ToArray()));
                            break;
                        case ObjectType.Relay:
                            Relay selectedRelay = fromSelected[pS] as Relay;
                            returnSelected.Add(AddRelay(selectedRelay.Position));
                            break;
                        case ObjectType.Spawn:
                            Spawn selectedSpawn = fromSelected[pS] as Spawn;
                            returnSelected.Add(AddSpawn(selectedSpawn.Position, selectedSpawn.SpawnID, selectedSpawn.Count, selectedSpawn.Delay));
                            break;
                        case ObjectType.Structure:
                            PalettedStructure selectedStructure = fromSelected[pS] as PalettedStructure;
                            CustomVD[] newVerts = new CustomVD[selectedStructure.VertexCount];
                            Int16[] newInds = new Int16[selectedStructure.IndexCount];

                            Vertices.CopyTo(selectedStructure.StartVertex, newVerts, 0, newVerts.Length);
                            Indices.CopyTo(selectedStructure.StartIndex, newInds, 0, newInds.Length);

                            // Adjust indices
                            for (int i = 0; i < newInds.Length; i++)
                            { newInds[i] -= (Int16)selectedStructure.StartVertex; }

                            returnSelected.Add(AddStructure(newVerts, newInds, selectedStructure.Position, selectedStructure.Forward,
                                selectedStructure.Up, selectedStructure.TexStart, selectedStructure.TexDimension,
                                selectedStructure.WRadius, selectedStructure.HRadius, selectedStructure.DRadius));
                            break;
                    }
                }
                return returnSelected.ToArray();
            }

            public MapEntity AddProximityTrigger(MapEntity newTarget, Location newLocation, int newOccurrences)
            {
                ProximityTrigger newTrigger = new ProximityTrigger(newTarget, newLocation, newOccurrences);
                newTrigger.Deactivate();
                Triggers.Add(newTrigger);
                Entities.Add(newTrigger);
                return newTrigger;
            }

            public MapEntity AddRelay(Vector3 newPos)
            {
                Relay newRelay = new Relay(newPos);
                Entities.Add(newRelay);
                lstRelays.Add(newRelay);
                return newRelay;
            }

            public MapEntity AddSpawn(Vector3 newPos, int commandIndex, int newCount, float newDelay)
            {
                Spawn newSpawn = new Spawn(newPos, commandIndex, newCount, newDelay);
                Entities.Add(newSpawn);
                Spawns.Add(newSpawn);
                return newSpawn;
            }

            public MapEntity AddTimedTrigger(MapEntity newTarget, float newDelay, int newOccurrences)
            {
                TimedTrigger newTrigger = new TimedTrigger(newTarget, newDelay, newOccurrences);
                newTrigger.Deactivate();
                Triggers.Add(newTrigger);
                Entities.Add(newTrigger);
                return newTrigger;
            }

            public void BakeLights()
            {
                Color defaultColor = new Color(30, 30, 30, 255);
                for (int i = 0; i < Entities.Count; i++)
                {
                    if (Entities[i] is IVertexed)
                    {
                        for (int a = ((IVertexed)Entities[i]).StartVertex; a < ((IVertexed)Entities[i]).StartVertex + ((IVertexed)Entities[i]).VertexCount; a++)
                        {
                            CustomVD vertHold = Vertices[a];
                            vertHold.LightColor = defaultColor;

                            for (int b = 0; b < Lights.Count; b++)
                            {
                                Vector3 lightDirection = Vector3.Normalize(vertHold.Position - Lights[b].Position);
                                float lightDistance = Vector3.Distance(vertHold.Position, Lights[b].Position);
                                float lightModifier = Lights[b].Intensity / (lightDistance * lightDistance);

                                Ray vertRay = new Ray(Lights[b].Position, lightDirection);
                                //MapEntity nearestStructure;
                                //float nearestBlock = IntersectsStructure(vertRay, out nearestStructure);

                                //if ((nearestStructure == null) || (nearestStructure == Entities[i]) || (nearestBlock > lightDistance * THRESH_OVERLAP))
                                //{
                                    Color newColor = vertHold.LightColor;
                                    float dotAdjust = Vector3.Dot(lightDirection, vertHold.Normal);
                                    lightModifier *= (1 - dotAdjust) * (1 - dotAdjust);

                                    newColor = Color.Lerp(newColor, Color.White, lightModifier);
                                    vertHold.LightColor = newColor;
                                //}
                            }
                            Vertices[a] = vertHold;
                        }
                    } // if vertexed
                } // for Selectables
            }
            /*
            public CollisionData Collision(BoundingSphere searchSphere, Vector3 sphereVelocity, float sphereFriction, bool accessToTiggers, SelectableObject referenceID)
            {
                CollisionData returnLink = new CollisionData(false, searchSphere.Center, sphereVelocity, false, Vector3.Zero);
                float sphereSpeed = sphereVelocity.Length();
                Vector3 sphereDirection = Vector3.Normalize(sphereVelocity);

                #region BOUNDS
                for (int a = 0; a < Bounds.Count; a++)
                {
                    if (Bounds[a] != referenceID)
                    {
                        bool isCollision = false;
                        Vector3 worldSurface = Vector3.Zero;
                        Nullable<float> hitDistance = 0;
                        BoundBox boxHold = Bounds[a] as BoundBox;
                        if (boxHold != null)
                        {
                            Matrix wallMat = Matrix.CreateWorld(boxHold.Position, boxHold.Forward, boxHold.Up);
                            Vector3 orientedBase = Vector3.Transform(searchSphere.Center - sphereVelocity, Matrix.Invert(wallMat));
                            Vector3 orientedDirection = Vector3.TransformNormal(sphereDirection, Matrix.Invert(wallMat));
                            
                            Ray testRay = new Ray(orientedBase + searchSphere.Radius * orientedDirection, orientedDirection);
                            hitDistance = testRay.Intersects((BoundingBox)boxHold.Bounds);

                            // if collision
                            if ((hitDistance.HasValue) && (hitDistance.Value <= sphereSpeed))
                            {
                                int planeIndex = -1;
                                //searchSphere.Center -= sphereVelocity;
                                //searchSphere.Center += hitDistance.Value * sphereDirection;
                                //orientedBase += hitDistance.Value * orientedDirection;
                                isCollision = true;

                                Vector3 strengthPercent = orientedBase + hitDistance.Value * orientedDirection;
                                strengthPercent.X /= boxHold.WRadius;
                                strengthPercent.Y /= boxHold.HRadius;
                                strengthPercent.Z /= boxHold.DRadius;
                                Vector3 uStrength = new Vector3(Math.Abs(strengthPercent.X), Math.Abs(strengthPercent.Y), Math.Abs(strengthPercent.Z));

                                // Prepare planes
                                aryBoundingPlanes[PLANE_UP].D = -boxHold.HRadius;
                                aryBoundingPlanes[PLANE_DOWN].D = -boxHold.HRadius;
                                aryBoundingPlanes[PLANE_LEFT].D = -boxHold.WRadius;
                                aryBoundingPlanes[PLANE_RIGHT].D = -boxHold.WRadius;
                                aryBoundingPlanes[PLANE_FORWARD].D = -boxHold.DRadius;
                                aryBoundingPlanes[PLANE_BACKWARD].D = -boxHold.DRadius;

                                // Left or Right
                                if ((uStrength.X >= uStrength.Y) && (uStrength.X >= uStrength.Z))
                                {
                                    // Right
                                    if (strengthPercent.X > 0) { planeIndex = PLANE_RIGHT; worldSurface = boxHold.Right; }
                                    // Default Left
                                    else { planeIndex = PLANE_LEFT; worldSurface = boxHold.Left; }
                                }
                                // else Up or Down
                                else if ((uStrength.Y >= uStrength.X) && (uStrength.Y >= uStrength.Z))
                                {
                                    // Down
                                    if (strengthPercent.Y < 0) { planeIndex = PLANE_DOWN; worldSurface = boxHold.Down; }
                                    // Default Up
                                    else { planeIndex = PLANE_UP; worldSurface = boxHold.Up; }
                                }
                                // else Forward or Backward
                                else if ((uStrength.Z >= uStrength.X) && (uStrength.Z >= uStrength.Y))
                                {
                                    // Backward
                                    if (strengthPercent.Z > 0) { planeIndex = PLANE_BACKWARD; worldSurface = boxHold.Backward; }
                                    // Default Forward
                                    else { planeIndex = PLANE_FORWARD; worldSurface = boxHold.Forward; }
                                }
                                // Update
                                if (planeIndex != -1)
                                {
                                    float finalAdjust = (searchSphere.Radius - Vector3.Dot(aryBoundingPlanes[planeIndex].Normal, orientedBase))
                                        / Vector3.Dot(aryBoundingPlanes[planeIndex].Normal, sphereSpeed * orientedDirection);
                                    //float finalAdjust = Vector3.Dot(aryBoundingPlanes[planeIndex].Normal, orientedBase + (hitDistance.Value * orientedDirection)
                                    //    - searchSphere.Radius * aryBoundingPlanes[planeIndex].Normal) + aryBoundingPlanes[planeIndex].D;
                                    searchSphere.Center -= finalAdjust * sphereDirection;// -(finalAdjust * worldSurface);
                                }
                            }
                        }
                        //}
                        // if sphere
                        else
                        {
                            BoundSphere sphereHold = Bounds[a] as BoundSphere;
                            if (sphereHold != null)
                            {
                                if (searchSphere.Intersects((BoundingSphere)sphereHold.Bounds))
                                {
                                    worldSurface = Vector3.Normalize(searchSphere.Center - sphereHold.Position);
                                    //hitDistance = (sphereHold.Radius + searchSphere.Radius) - Vector3.Distance(sphereHold.Position, searchSphere.Center);
                                    //searchSphere.Center += hitDistance.Value * worldSurface;
                                }
                            }
                        }
                        // Finalize collision
                        if (isCollision)
                        {
                            Vector3 reflection = Vector3.Reflect(sphereDirection, worldSurface);
                            sphereVelocity = sphereSpeed * reflection;

                            //if (Bounds[a].IsAnimated)
                            //{ sphereVelocity += Bounds[a].Position - ((IResponsive)Bounds[a]).PreviousState.Position; }

                            // Apply collision physics
                            Vector3 vY = Vector3.Dot(sphereVelocity, worldSurface) * worldSurface;
                            Vector3 vX = sphereVelocity - vY;
                            float dForce = vY.Length();

                            // Friction
                            vX *= 1 - (sphereFriction * Bounds[a].Friction);
                            // Elasticity
                            vY *= dForce * Bounds[a].Elasticity * Math.Abs(Vector3.Dot(sphereDirection, reflection));
                            // Ignore tiny bounces
                            if (vY.Length() < THRESH_BOUNCE) { vY = Vector3.Zero; }

                            // Update
                            sphereVelocity = vX + vY;
                            sphereSpeed = sphereVelocity.Length();
                            sphereDirection = Vector3.Normalize(sphereVelocity);
                            searchSphere.Center += sphereVelocity;

                            if (Vector3.Dot(Vector3.Up, worldSurface) > .5f)
                            { returnLink.OnSteadyGround = true; returnLink.GroundNormal = worldSurface; }
                            returnLink.Collision = true;
                        }
                    }
                }
                #endregion

                // Locations - if access
                if (accessToTiggers)
                {
                    for (int a = 0; a < Locations.Count; a++)
                    {
                        LocationBox locBox = Locations[a] as LocationBox;
                        if (locBox != null)
                        {
                            Matrix boxWorld = locBox.World;
                            BoundingSphere testSphere = new BoundingSphere(Vector3.Transform(searchSphere.Center, Matrix.Invert(boxWorld)), searchSphere.Radius);
                            if (((BoundingBox)Locations[a].Bounds).Intersects(testSphere))
                            { Locations[a].IsInside = true; }
                            else
                            { Locations[a].IsInside = false; }
                        }
                        else
                        {
                            LocationSphere locSphere = Locations[a] as LocationSphere;
                            if (locSphere != null)
                            {
                                if (((BoundingSphere)Locations[a].Bounds).Intersects(searchSphere))
                                { Locations[a].IsInside = true; }
                                else
                                { Locations[a].IsInside = false; }
                            }
                        }
                    }
                }
                returnLink.ShiftPosition = searchSphere.Center - returnLink.ShiftPosition;
                returnLink.VelocityResult = sphereVelocity;
                return returnLink;
            }
            */
            public CollisionData Collision(BoundingSphere searchSphere, Vector3 sphereVelocity, float sphereFriction, bool accessToTiggers, MapEntity referenceID)
            {
                //CollisionData returnLink = new CollisionData(false, searchSphere.Center, sphereVelocity, false, Vector3.Zero);
                bool isCollision = false;
                List<Vector3> lstSurfaces = new List<Vector3>(3);
                List<Nullable<float>> lstDistances = new List<Nullable<float>>(3);
                List<float> lstElasticities = new List<float>(3);
                List<float> lstFrictions = new List<float>(3);

                float sphereSpeed = sphereVelocity.Length();
                Vector3 sphereDirection = Vector3.Normalize(sphereVelocity);

                #region BOUNDS
                for (int a = 0; a < Bounds.Count; a++)
                {
                    if (Bounds[a] != referenceID)
                    {
                        Vector3 worldSurface = Vector3.Zero;
                        Nullable<float> hitDistance = 0;

                        BoundBox boxHold = Bounds[a] as BoundBox;
                        if (boxHold != null)
                        {
                            Matrix wallMat = Matrix.CreateWorld(boxHold.Position, boxHold.Forward, boxHold.Up);
                            Vector3 orientedBase = Vector3.Transform(searchSphere.Center, Matrix.Invert(wallMat));
                            Vector3 orientedDirection = Vector3.TransformNormal(sphereDirection, Matrix.Invert(wallMat));
                            BoundingSphere testSphere = new BoundingSphere(orientedBase, searchSphere.Radius);

                            // if general collision
                            if (testSphere.Intersects(((BoundingBox)boxHold.CollisionBound)))
                            {
                                isCollision = true;

                                // Determine plane hit
                                int planeIndex = -1;
                                Vector3 strengthPercent = orientedBase - (searchSphere.Radius) * -Vector3.Normalize(orientedBase);
                                strengthPercent.X /= boxHold.WRadius;
                                strengthPercent.Y /= boxHold.HRadius;
                                strengthPercent.Z /= boxHold.DRadius;

                                if ((Math.Abs(strengthPercent.X) > 1) && (Math.Abs(strengthPercent.X) > Math.Abs(strengthPercent.Y))
                                    && (Math.Abs(strengthPercent.X) > Math.Abs(strengthPercent.Z)))
                                {
                                    if (strengthPercent.X > 0)
                                    { worldSurface = boxHold.Right; planeIndex = PLANE_RIGHT; }
                                    else if (strengthPercent.X < 0)
                                    { worldSurface = boxHold.Left; planeIndex = PLANE_LEFT; }
                                }
                                else if ((Math.Abs(strengthPercent.Y) > 1) && (Math.Abs(strengthPercent.Y) > Math.Abs(strengthPercent.X))
                                    && (Math.Abs(strengthPercent.Y) > Math.Abs(strengthPercent.Z)))
                                {
                                    if (strengthPercent.Y < 0)
                                    { worldSurface = boxHold.Down; planeIndex = PLANE_DOWN; }
                                    else if (strengthPercent.Y > 0)
                                    { worldSurface = boxHold.Up; planeIndex = PLANE_UP; }
                                }
                                else if ((Math.Abs(strengthPercent.Z) > 1) && (Math.Abs(strengthPercent.Z) > Math.Abs(strengthPercent.X))
                                    && (Math.Abs(strengthPercent.Z) > Math.Abs(strengthPercent.Y)))
                                {
                                    if (strengthPercent.Z > 0)
                                    { worldSurface = boxHold.Backward; planeIndex = PLANE_BACKWARD; }
                                    else if (strengthPercent.Z < 0)
                                    { worldSurface = boxHold.Forward; planeIndex = PLANE_FORWARD; }
                                }

                                if (planeIndex != -1)
                                {
                                    isCollision = true;
                                    // Prepare planes
                                    aryBoundingPlanes[PLANE_UP].D = -boxHold.HRadius;
                                    aryBoundingPlanes[PLANE_DOWN].D = -boxHold.HRadius;
                                    aryBoundingPlanes[PLANE_LEFT].D = -boxHold.WRadius;
                                    aryBoundingPlanes[PLANE_RIGHT].D = -boxHold.WRadius;
                                    aryBoundingPlanes[PLANE_FORWARD].D = -boxHold.DRadius;
                                    aryBoundingPlanes[PLANE_BACKWARD].D = -boxHold.DRadius;

                                    Ray testRay = new Ray(testSphere.Center + (testSphere.Radius * -aryBoundingPlanes[planeIndex].Normal), -orientedDirection);
                                    hitDistance = -Vector3.Dot(Vector3.Normalize(aryBoundingPlanes[planeIndex].Normal), testRay.Position) - aryBoundingPlanes[planeIndex].D;
                                    if (hitDistance.HasValue)
                                    {
                                        lstSurfaces.Add(worldSurface);
                                        lstDistances.Add(hitDistance);
                                        lstElasticities.Add(boxHold.Elasticity);
                                        lstFrictions.Add(boxHold.Friction);
                                        //searchSphere.Center += hitDistance.Value * worldSurface;
                                    }
                                    //else { hitDistance = 0; }
                                }
                            }
                        }
                        //}
                        // if sphere
                        else
                        {
                            /*BoundSphere sphereHold = Bounds[a] as BoundSphere;
                            if (sphereHold != null)
                            {
                                if (searchSphere.Intersects((BoundingSphere)sphereHold.CollisionBound))
                                {
                                    isCollision = true;
                                    worldSurface = Vector3.Normalize(searchSphere.Center - sphereHold.Position);
                                    hitDistance = (sphereHold.Radius + searchSphere.Radius) - Vector3.Distance(sphereHold.Position, searchSphere.Center);
                                    searchSphere.Center += hitDistance.Value * worldSurface;
                                }
                            }*/
                        }
                        // Finalize collision
                        /*if (isCollision)
                        {
                            Vector3 reflection = Vector3.Reflect(sphereDirection, worldSurface);
                            sphereVelocity = sphereSpeed * reflection;

                            // Apply collision physics
                            Vector3 vY = Vector3.Dot(sphereVelocity, worldSurface) * worldSurface;
                            Vector3 vX = sphereVelocity - vY;
                            float dForce = vY.Length();

                            // Friction
                            if (dForce != 0)
                            { vX *= (1 - .9f * (dForce / sphereSpeed)) * (1 - (sphereFriction * Bounds[a].Friction)); }
                            // Elasticity
                            vY *= dForce * Bounds[a].Elasticity * Math.Abs(Vector3.Dot(sphereDirection, reflection));
                            // Ignore tiny bounces
                            if (vY.Length() < THRESH_BOUNCE) { vY = Vector3.Zero; }

                            // Update
                            sphereVelocity = vX + vY;
                            sphereSpeed = sphereVelocity.Length();
                            sphereDirection = Vector3.Normalize(sphereVelocity);
                            searchSphere.Center += sphereVelocity;

                            if (Vector3.Dot(Vector3.Up, worldSurface) > .5f)
                            { returnLink.OnSteadyGround = true; returnLink.GroundNormal = worldSurface; }
                        }*/
                    }
                }
                #endregion

                // Locations - if access
                if (accessToTiggers)
                {
                    for (int a = 0; a < Locations.Count; a++)
                    {
                        LocationBox locBox = Locations[a] as LocationBox;
                        if (locBox != null)
                        {
                            Matrix boxWorld = locBox.World;
                            BoundingSphere testSphere = new BoundingSphere(Vector3.Transform(searchSphere.Center, Matrix.Invert(boxWorld)), searchSphere.Radius);
                            if (((BoundingBox)Locations[a].Bounds).Intersects(testSphere))
                            { Locations[a].IsInside = true; }
                            else
                            { Locations[a].IsInside = false; }
                        }
                        else
                        {
                            LocationSphere locSphere = Locations[a] as LocationSphere;
                            if (locSphere != null)
                            {
                                if (((BoundingSphere)Locations[a].Bounds).Intersects(searchSphere))
                                { Locations[a].IsInside = true; }
                                else
                                { Locations[a].IsInside = false; }
                            }
                        }
                    }
                }
                CollisionData returnData = new CollisionData(isCollision, lstSurfaces.ToArray(), lstDistances.ToArray(), lstElasticities.ToArray(), lstFrictions.ToArray());
                return returnData;
            }

            public void CreateCollisionSphere()
            {
                if (Bounds.Count > 0)
                {
                    // Start from first bound 
                    BoundBox boxHold = Bounds[0] as BoundBox;
                    if (boxHold != null)
                    { bound = new BoundingSphere(boxHold.Position, boxHold.Max.Length()); }
                    else
                    {
                        BoundSphere sphereHold = Bounds[0] as BoundSphere;
                        if (sphereHold != null) { bound = (BoundingSphere)sphereHold.CollisionBound; }
                    }

                    // Expand from remaining bounds 
                    for (int i = 1; i < Bounds.Count; i++)
                    {
                        boxHold = Bounds[i] as BoundBox;
                        if (boxHold != null)
                        { bound = BoundingSphere.CreateMerged(bound, new BoundingSphere(boxHold.Position, boxHold.Max.Length())); }
                        else
                        {
                            BoundSphere sphereHold = Bounds[i] as BoundSphere;
                            if (sphereHold != null)
                            { bound = BoundingSphere.CreateMerged(bound, (BoundingSphere)sphereHold.CollisionBound); }
                        }
                    }
                }
            }

            public void DeactivateObject(params MapEntity[] fromSelected) 
            { 
                for (int pS = 0; pS < fromSelected.Length; pS++) 
                { 
                        if ((fromSelected[pS] is IActivatable) && ((IActivatable)fromSelected[pS]).IsActive)
                        {
                            ((IActivatable)fromSelected[pS]).Deactivate();

                            if (fromSelected[pS] is ICollidable)
                            { lstCollidables.Remove(fromSelected[pS]); }

                            if (fromSelected[pS] is IUpdatable)
                            { lstUpdatables.Remove(fromSelected[pS]); } 
                        } 
                } 
            } 

            public void DeleteObject(params MapEntity[] fromSelected)
            {
                for (int pS = 0; pS < fromSelected.Length; pS++)
                {
                    // Process any attachments
                    if (fromSelected[pS] is IPinnable)
                    {
                        if (((IPinnable)fromSelected[pS]).IsPinned)
                        {
                            // Unpin from Pin
                            ((IPinnable)fromSelected[pS]).IsPinned = false;
                            ((IPin)((IPinnable)fromSelected[pS]).Pin).RemovePinned(fromSelected[pS]);
                        }
                    }
                    if (fromSelected[pS] is IAnimatable)
                    {
                        if (((IAnimatable)fromSelected[pS]).IsAnimated)
                        {
                            // Unlink from animation
                            for (int i = 0; i < Animations.Count; i++)
                            { Animations[i].RemoveObject(fromSelected[pS]); }
                        }
                    }
                    // Remove object
                    switch (fromSelected[pS].Type)
                    {
                        case ObjectType.Animation:
                            Animation animHold = fromSelected[pS] as Animation;
                            // Unlink objects
                            animHold.ClearObjects();
                            lstAnimations.Remove(animHold);
                            break;
                        case ObjectType.Bound:
                            lstBounds.Remove(fromSelected[pS] as Bound);
                            break;
                        case ObjectType.Light:
                            lstLights.Remove(fromSelected[pS] as Light);
                            break;
                        case ObjectType.Location:
                            lstLocations.Remove(fromSelected[pS] as Location);
                            break;
                        case ObjectType.Pinned:
                            // Rercurse through each pinned object
                            for (int i = 0; i < ((IPin)fromSelected[pS]).Pinned.Length; i++)
                            {
                                // Unpin and delete pinned
                                ((IPinnable)((IPin)fromSelected[pS]).Pinned[i]).IsPinned = false;
                                DeleteObject(((IPin)fromSelected[pS]).Pinned[i]);
                            }
                            // Remove Pin
                            lstPinned.Remove(fromSelected[pS] as PinnedStructure);
                            break;
                        case ObjectType.Relay:
                            lstRelays.Remove(fromSelected[pS] as Relay);
                            break;
                        case ObjectType.Spawn:
                            lstSpawns.Remove(fromSelected[pS] as Spawn);
                            break;
                        case ObjectType.Structure:
                            RemoveStructure(fromSelected[pS] as PalettedStructure);
                            break;
                        case ObjectType.Trigger:
                            lstTriggers.Remove(fromSelected[pS] as Trigger);
                            break;
                    }

                    if ((fromSelected[pS] is IActivatable) && ((IActivatable)fromSelected[pS]).IsActive)
                    { DeactivateObject(fromSelected[pS]); }

                    // Remove from Entities
                    Entities.Remove(fromSelected[pS]);
                }
            }

            public void FlagAnimation(object sender, FlagArg flagArg)
            {
                
            }

            private void FlagVertices(object sender, FlagArg flagArg)
            { lstFlags.Add(SectorFlag.ModifiedVertices); }

            public MapEntity IdentifyEntity(int searchID)
            {
                MapEntity returnEntity = null;
                for (int i = 0; i < Entities.Count; i++)
                { if (Entities[i].ID == searchID) { returnEntity = Entities[i]; break; } }
                return returnEntity;
            }

            public bool Intersects(SelectableObject firstObject, SelectableObject secondObject)
            {
                bool intersecting = false;
                // Determine first object
                if (firstObject is IDimensioned)
                {
                    BoundingBox firstBox = new BoundingBox(-((IDimensioned)firstObject).Max, ((IDimensioned)firstObject).Max);
                    Matrix firstWorld = Matrix.Identity;
                    if (firstObject is IOriented) { firstWorld = ((IOriented)firstObject).World; }
                    
                    // Determine second object
                    if (secondObject is IDimensioned)
                    {
                        BoundingBox secondBox = new BoundingBox(-((IDimensioned)secondObject).Max, ((IDimensioned)secondObject).Max);
                        Matrix secondWorld = Matrix.Identity;
                        if (secondObject is IOriented) { secondWorld = ((IOriented)secondObject).World; }

                    }
                    else if (secondObject is IRadial)
                    {
                        BoundingSphere secondSphere = new BoundingSphere(Vector3.Transform(secondObject.Position, Matrix.Invert(firstWorld)),((IRadial)secondObject).Radius);
                        if (secondSphere.Intersects(firstBox)) { intersecting = true; }
                    }
                }
                else if (firstObject is IRadial)
                {
                    BoundingSphere firstSphere = new BoundingSphere(firstObject.Position, ((IRadial)firstObject).Radius);
                    
                    // Determine second object
                    if (secondObject is IDimensioned)
                    {
                        BoundingBox secondBox = new BoundingBox(-((IDimensioned)secondObject).Max, ((IDimensioned)secondObject).Max);
                        Matrix secondWorld = Matrix.Identity;
                        if (secondObject is IOriented) { secondWorld = ((IOriented)secondObject).World; }

                        firstSphere.Center = Vector3.Transform(firstSphere.Center, Matrix.Invert(secondWorld));
                        if (secondBox.Intersects(firstSphere)) { intersecting = true; }
                    }
                    else if (secondObject is IRadial)
                    {
                        BoundingSphere secondSphere = new BoundingSphere(secondObject.Position, ((IRadial)secondObject).Radius);
                        if (secondSphere.Intersects(firstSphere)) { intersecting = true; }
                    }
                }
                return intersecting;
            }

            public float IntersectsStructure(Ray searchRay, out MapEntity hitEntity)
            {
                Ray testRay = searchRay;
                float testDistance = 0;
                float foundDistance = -1;
                hitEntity = null;

                for (int a = 0; a < Structures.Count; a++)
                {
                    PalettedStructure structHold = Structures[a];
                    BoundingBox testBox = new BoundingBox(new Vector3(-structHold.WRadius, -structHold.HRadius, -structHold.DRadius),
                        new Vector3(structHold.WRadius, structHold.HRadius, structHold.DRadius));
                    // Orient ray
                    Matrix testMat = Matrix.Invert(Matrix.CreateWorld(structHold.Position, structHold.Forward, structHold.Up));
                    testRay.Position = Vector3.Transform(searchRay.Position, testMat);
                    testRay.Direction = Vector3.TransformNormal(searchRay.Direction, testMat);

                    if (testRay.Intersects(testBox) != null)
                    {
                        testDistance = (float)testRay.Intersects(testBox);
                        if ((testDistance < foundDistance) || (foundDistance == -1))
                        { foundDistance = testDistance; hitEntity = Structures[a]; }
                    }
                }
                return foundDistance;
            }

            public void LinkObjects(params MapEntity[] fromSelected)
            {
                // Sort Objects by type
                List<Relay> lstRelays = new List<Relay>();
                for (int i = 0; i < fromSelected.Length; i++)
                {
                    switch (fromSelected[i].Type)
                    {
                        case ObjectType.Relay:
                            lstRelays.Add(fromSelected[i] as Relay);
                            break;
                    }
                }

                // Link Relays
                for (int i = 0; i < lstRelays.Count; i++)
                {
                    for (int a = 0; a < lstRelays.Count; a++)
                    {
                        if (i != a)
                        { lstRelays[i].LinkRelay(lstRelays[a]); }
                    }
                }
            }

            // Modify Texture Coordinates
            public void ModifyObject(Vector2 newTexStart, Vector2 newTexDimension, params MapEntity[] fromSelected)
            {
                for (int pS = 0; pS < fromSelected.Length; pS++)
                {
                    PinnedStructure pinHold = fromSelected[pS] as PinnedStructure;
                    if (pinHold != null)
                    {
                        for (int i = 0; i < pinHold.Pinned.Length; i++)
                        { ModifyObject(newTexStart, newTexDimension, pinHold.Pinned[i]); }
                    }
                    else
                    {
                        PalettedStructure structHold = fromSelected[pS] as PalettedStructure;
                        if (structHold != null)
                        { ModifyTexCoords(structHold, newTexStart, newTexDimension); }
                    }
                }
            }

            // Modify Intensity / Scale
            public void ModifyObject(float intensityAmount, bool specialMod, params MapEntity[] fromSelected)
            {
                for (int pS = 0; pS < fromSelected.Length; pS++)
                {
                    Light isLight = fromSelected[pS] as Light;
                    if (isLight != null)
                    {
                        // Light - Special mod changes range
                        if (!specialMod)
                        {
                            float newIntensity = isLight.Intensity + intensityAmount;
                            if (newIntensity > THRESH_INTENSITY)
                            { isLight.Intensity = newIntensity; }
                            else
                            { isLight.Intensity = THRESH_INTENSITY; }
                        }
                        else
                        {
                            float newRange = isLight.Range + -intensityAmount;
                            if (newRange > 1) { newRange = 1; }
                            else if (newRange < -1) { newRange = -1; }
                            isLight.Range = newRange;
                        }
                    }
                    else
                    { ModifyObject(intensityAmount, intensityAmount, intensityAmount, fromSelected[pS]); }

                } // for selected
            }

            // Modify Positions
            public void ModifyObject(Vector3 shiftPositions, params MapEntity[] fromSelected)
            {
                for (int pS = 0; pS < fromSelected.Length; pS++)
                {
                    if (fromSelected[pS] is IObject)
                    { ((IObject)fromSelected[pS]).Position += shiftPositions; }

                    if (fromSelected[pS] is IVertexed)
                    {
                        for (int i = 0; i < ((IVertexed)fromSelected[pS]).VertexCount; i++)
                        {
                            CustomVD vertexPass = Vertices[((IVertexed)fromSelected[pS]).StartVertex + i];
                            vertexPass.Position += shiftPositions;
                            Vertices[((IVertexed)fromSelected[pS]).StartVertex + i] = vertexPass;
                        }
                    }

                    // Recurse pinned
                    if (fromSelected[pS] is IPin)
                    {
                        for (int i = 0; i < ((IPin)fromSelected[pS]).Pinned.Length; i++)
                        { ModifyObject(shiftPositions, ((IPin)fromSelected[pS]).Pinned[i]); }
                    }
                }
            }

            // Modify Rotation
            public void ModifyObject(Matrix rotationMatrix, params MapEntity[] fromSelected)
            {
                for (int pS = 0; pS < fromSelected.Length; pS++)
                {
                    SelectableObject selectHold = fromSelected[pS] as SelectableObject;
                    if (selectHold != null)
                    {
                        Matrix transMat = Matrix.CreateTranslation(((IObject)fromSelected[pS]).Position);
                        RotateObject(selectHold, rotationMatrix, transMat);
                    }
                }
            }

            // Modify Specific Scale
            public void ModifyObject(float intensityWidth, float intensityHeight, float intensityDepth, params MapEntity[] fromSelected)
            {
                for (int pS = 0; pS < fromSelected.Length; pS++)
                {
                    if (fromSelected[pS] is IDimensioned)
                    {
                        float wHold = ((IDimensioned)fromSelected[pS]).WRadius;
                        float hHold = ((IDimensioned)fromSelected[pS]).HRadius;
                        float dHold = ((IDimensioned)fromSelected[pS]).DRadius;

                        float newIntensityWidth = (wHold + intensityWidth) / wHold;
                        float newIntensityHeight = (hHold + intensityHeight) / hHold;
                        float newIntensityDepth = (dHold + intensityDepth) / dHold;

                        wHold *= newIntensityWidth;
                        hHold *= newIntensityHeight;
                        dHold *= newIntensityDepth;

                        if (wHold >= GridSize) { ((IDimensioned)fromSelected[pS]).WRadius = wHold; }
                        if (hHold >= GridSize) { ((IDimensioned)fromSelected[pS]).HRadius = hHold; }
                        if (dHold >= GridSize) { ((IDimensioned)fromSelected[pS]).DRadius = dHold; }

                        if (fromSelected[pS] is IVertexed)
                        {
                            Matrix selectedWorld = Matrix.Identity;
                            Matrix selectedScale = Matrix.Identity;
                            Vector3 selectedPos = Vector3.Zero, selectedForward = Vector3.Forward, selectedUp = Vector3.Up;

                            if (fromSelected[pS] is IObject)
                            { selectedPos = ((IObject)fromSelected[pS]).Position; }

                            if (fromSelected[pS] is IOriented)
                            {
                                selectedForward = ((IOriented)fromSelected[pS]).Forward;
                                selectedUp = ((IOriented)fromSelected[pS]).Up;
                            }
                            selectedWorld = Matrix.CreateWorld(selectedPos, selectedForward, selectedUp);

                            if (wHold >= GridSize) { selectedScale *= Matrix.CreateScale(newIntensityWidth, 1, 1); }
                            if (hHold >= GridSize) { selectedScale *= Matrix.CreateScale(1, newIntensityHeight, 1); }
                            if (dHold >= GridSize) { selectedScale *= Matrix.CreateScale(1, 1, newIntensityDepth); }

                            // Update vertices
                            for (int i = ((IVertexed)fromSelected[pS]).StartVertex; i < ((IVertexed)fromSelected[pS]).StartVertex + ((IVertexed)fromSelected[pS]).VertexCount; i++)
                            {
                                CustomVD vertPass = Vertices[i];
                                vertPass.Position = Vector3.Transform(vertPass.Position, Matrix.Invert(selectedWorld) * selectedScale * selectedWorld);
                                Vertices[i] = vertPass;
                            }
                        }
                    }

                    if (fromSelected[pS] is IRadial)
                    {
                        float rHold = ((IRadial)fromSelected[pS]).Radius;
                        float intensityAmount = (rHold + ((intensityWidth + intensityHeight + intensityDepth) / 3)) / rHold;
                        rHold *= intensityAmount;
                        if (rHold > GridSize) { ((IRadial)fromSelected[pS]).Radius = rHold; }

                        if (fromSelected[pS] is IVertexed)
                        {
                            Matrix selectedWorld = Matrix.Identity;
                            Matrix selectedScale = Matrix.Identity;
                            Vector3 selectedPos = Vector3.Zero, selectedForward = Vector3.Forward, selectedUp = Vector3.Up;

                            if (fromSelected[pS] is IObject)
                            { selectedPos = ((IObject)fromSelected[pS]).Position; }

                            if (fromSelected[pS] is IOriented)
                            {
                                selectedForward = ((IOriented)fromSelected[pS]).Forward;
                                selectedUp = ((IOriented)fromSelected[pS]).Up;
                            }
                            selectedWorld = Matrix.CreateWorld(selectedPos, selectedForward, selectedUp);

                            if (rHold > GridSize) { selectedScale *= Matrix.CreateScale(intensityAmount); }

                            // Update vertices
                            for (int i = ((IVertexed)fromSelected[pS]).StartVertex; i < ((IVertexed)fromSelected[pS]).StartVertex + ((IVertexed)fromSelected[pS]).VertexCount; i++)
                            {
                                CustomVD vertPass = Vertices[i];
                                vertPass.Position = Vector3.Transform(vertPass.Position, Matrix.Invert(selectedWorld) * selectedScale * selectedWorld);
                                Vertices[i] = vertPass;
                            }
                        }
                    }
                    // Recurse pinned
                    if (fromSelected[pS] is IPin)
                    {
                        for (int i = 0; i < ((IPin)fromSelected[pS]).Pinned.Length; i++)
                        { ModifyObject(intensityWidth, intensityHeight, intensityDepth, ((IPin)fromSelected[pS]).Pinned[i]); }
                    }
                } // for selected
            }

            // Pins all pinnables from selected and returns Pin
            public MapEntity PinObjects(params MapEntity[] fromSelected)
            {
                List<MapEntity> pinObjects = new List<MapEntity>();
                MapEntity returnPin = new MapEntity();

                for (int pS = 0; pS < fromSelected.Length; pS++)
                {
                    // if pinnable
                    if (fromSelected[pS] is IPinnable)
                    { pinObjects.Add(fromSelected[pS]); }
                }
                // Pin objects
                if (pinObjects.Count > 0)
                {
                    PinnedStructure newPin = new PinnedStructure();
                    Vector3 newPos = Vector3.Zero, newForward = Vector3.Zero, newUp = Vector3.Zero;
                    for (int i = 0; i < pinObjects.Count; i++)
                    {
                        if (pinObjects[i] is IObject)
                        { newPos += ((IObject)pinObjects[i]).Position; }
                        if (pinObjects[i] is IOriented)
                        {
                            newForward += ((IOriented)pinObjects[i]).Forward;
                            newUp += ((IOriented)pinObjects[i]).Up;
                        }
                        // Set Pin, mark pinned, add pinnable
                        ((IPinnable)pinObjects[i]).Pin = newPin;
                        ((IPinnable)pinObjects[i]).IsPinned = true;
                        newPin.AddPinned(pinObjects[i]);
                    }
                    // Finalize pin
                    newPin.Position = newPos / pinObjects.Count;
                    newPin.Forward = newForward / pinObjects.Count;
                    newPin.Up = newUp / pinObjects.Count;

                    Entities.Add(newPin);
                    PinnedStructures.Add(newPin);
                    returnPin = newPin;
                }
                return returnPin;
            }

            public void ResetObject(params MapEntity[] fromSelected) 
            { 
                for (int pS = 0; pS < fromSelected.Length; pS++) 
                {
                    InteractiveStructure structHold = fromSelected[pS] as InteractiveStructure;
                    if (structHold != null) 
                    {
                        Matrix oldWorld = structHold.World;
                        structHold.Reset();
                        Matrix transWorld = Matrix.Invert(oldWorld) * structHold.World;
                        // Update vertices
                        for (int a = structHold.StartVertex; a < structHold.StartVertex + structHold.VertexCount; a++)
                        {
                            CustomVD vertexPass = Vertices[a];
                            vertexPass.Position = Vector3.Transform(vertexPass.Position, transWorld);
                            vertexPass.Normal = Vector3.TransformNormal(vertexPass.Normal, transWorld);
                            Vertices[a] = vertexPass;
                        }
                    }
                    // else non-visual
                    else
                    {
                        ((IActivatable)fromSelected[pS]).Reset();
                        switch (fromSelected[pS].Type)
                        {
                            case ObjectType.Animation:
                                SetAnimation(((Animation)fromSelected[pS]).PlayPosition, fromSelected[pS]);
                                break;
                            case ObjectType.Trigger:
                                ResetObject(((Trigger)fromSelected[pS]).TargetObject);
                                break;
                        }
                    }
                } 
            } 

            public MapEntity SelectObject(LayerType searchLayer, Ray searchRay, bool overridePin)
            {
                Ray testRay = searchRay;
                float testDistance = 0;
                float foundDistance = -1;
                MapEntity foundType = null;

                for (int i = 0; i < Entities.Count; i++)
                {
                    // if IObject and satisfies its search type
                    if ((Entities[i] is IObject) &&
                        (((Entities[i].Type == ObjectType.Bound) && (searchLayer == LayerType.Bound))
                        || ((Entities[i].Type == ObjectType.Light) && (searchLayer == LayerType.Light))
                        || ((Entities[i].Type == ObjectType.Location) && (searchLayer == LayerType.Location))
                        || ((Entities[i].Type == ObjectType.Relay) && (searchLayer == LayerType.Location))
                        || ((Entities[i].Type == ObjectType.Spawn) && (searchLayer == LayerType.Location))
                        || ((Entities[i].Type == ObjectType.Structure) && (searchLayer == LayerType.Construction))))
                    {
                        Vector3 testPosition = ((IObject)Entities[i]).Position;
                        if (Entities[i] is IDimensioned)
                        {
                            float testW = ((IDimensioned)Entities[i]).WRadius;
                            float testH = ((IDimensioned)Entities[i]).HRadius;
                            float testD = ((IDimensioned)Entities[i]).DRadius;
                            Vector3 testForward = Vector3.Forward;
                            Vector3 testUp = Vector3.Up;

                            if (Entities[i] is IOriented)
                            {
                                testForward = ((IOriented)Entities[i]).Forward;
                                testUp = ((IOriented)Entities[i]).Up;
                            }
                            // Create bound and test
                            BoundingBox testBox = new BoundingBox(new Vector3(-testW, -testH, -testD), new Vector3(testW, testH, testD));
                            // Orient ray
                            Matrix testWorld = Matrix.Invert(Matrix.CreateWorld(testPosition, testForward, testUp));
                            testRay.Position = Vector3.Transform(searchRay.Position, testWorld);
                            testRay.Direction = Vector3.TransformNormal(searchRay.Direction, testWorld);
                            if (testRay.Intersects(testBox) != null)
                            {
                                testDistance = (float)testRay.Intersects(testBox);
                                if ((testDistance < foundDistance) || (foundDistance == -1))
                                {
                                    foundDistance = testDistance;
                                    foundType = Entities[i];
                                }
                            }
                        }
                        else if (Entities[i] is IRadial)
                        {
                            // Create bound and test
                            BoundingSphere testSphere = new BoundingSphere(testPosition, ((IRadial)Entities[i]).Radius);
                            if (searchRay.Intersects(testSphere) != null)
                            {
                                testDistance = (float)searchRay.Intersects(testSphere);
                                if ((testDistance < foundDistance) || (foundDistance == -1))
                                {
                                    foundDistance = testDistance;
                                    foundType = Entities[i];
                                }
                            }
                        }
                    } // if IObject
                }
                // Convert into Pinned if necessary
                if ((foundType != null) && (!overridePin))
                {
                    // Search up pinnable chain until final Pin is found
                    bool searchComplete = false;
                    while ((foundType is IPinnable) && (searchComplete == false))
                    {
                        if (((IPinnable)foundType).IsPinned)
                        { foundType = ((IPinnable)foundType).Pin; }
                        else { searchComplete = true; }
                    }
                }
                return foundType;
            }

            public void SetAnimation(float timePosition, params MapEntity[] fromSelected)
            {
                // Locate animation
                Animation animHold = null;
                for (int i = 0; i < fromSelected.Length; i++)
                {
                    if (fromSelected[i].Type == ObjectType.Animation)
                    { animHold = fromSelected[i] as Animation; break; }
                }
                if (animHold != null)
                {
                    MapEntity[] animatedObjects = animHold.AnimatedObjects;
                    animHold.PlayPosition = timePosition;
                    // Process each object if possible
                    for (int i = 0; i < animatedObjects.Length; i++)
                    { ApplyState(animatedObjects[i], animHold.GetState(i, timePosition)); }
                } // if animation found
            }

            public void SetAnimationState(float timePosition, params MapEntity[] fromSelected)
            {
                // Locate animation
                Animation animHold = null;
                for (int i = 0; i < fromSelected.Length; i++)
                {
                    if (fromSelected[i].Type == ObjectType.Animation)
                    { animHold = fromSelected[i] as Animation; break; }
                }
                if (animHold != null)
                {
                    // Process each object if possible
                    for (int i = 0; i < fromSelected.Length; i++)
                    {
                        int foundState = animHold.GetStateIndex(fromSelected[i]);
                        if (foundState != -1)
                        {
                            ObjectState stateHold = animHold.GetState(foundState, timePosition);
                            ApplyState(fromSelected[i], stateHold);
                        } // if state found
                    }
                } // if animation found
            }

            public SectorFlag[] TakeFlags()
            { SectorFlag[] returnFlags = lstFlags.ToArray(); lstFlags.Clear(); return returnFlags; }

            public void UnpinObjects(params MapEntity[] fromSelected)
            {
                List<SelectableObject> removePins = new List<SelectableObject>(fromSelected.Length);
                for (int pS = 0; pS < fromSelected.Length; pS++)
                {
                    PinnedStructure pinHold = fromSelected[pS] as PinnedStructure;
                    if (pinHold != null)
                    {
                        // Unpin objects
                        for (int i = 0; i < pinHold.Pinned.Length; i++)
                        {
                            ((IPinnable)pinHold.Pinned[i]).IsPinned = false;
                            ((IPinnable)pinHold.Pinned[i]).Pin = null;
                        }
                        // Remove Pin
                        PinnedStructures.Remove(pinHold);
                        removePins.Add(pinHold);
                    }
                }
                // Remove pins from selectables
                for (int i = 0; i < removePins.Count; i++)
                { Entities.Remove(removePins[i]); }
            }

            public void Update(float tSeconds)
            {
                for (int i = 0; i < lstUpdatables.Count; i++)
                {
                    SelectableObject testObject = lstUpdatables[i] as SelectableObject;
                    if (testObject != null)
                    {
                        // Dynamics
                        if (testObject is IDynamic)
                        {
                            // Apply forces and update
                            ((IDynamic)testObject).ApplyForce(Gravity * Vector3.Down * tSeconds * tSeconds);
                            ((IUpdatable)testObject).Update(tSeconds);
                            Matrix dWorld = ((IDynamic)testObject).WorldVelocity;
                            // Collisions
                            if (testObject is ICollidable)
                            {
                                switch (((ICollidable)testObject).BoundType)
                                {
                                    case BoundType.Box:
                                        break;
                                    case BoundType.Sphere:
                                        break;
                                }
                            }

                            if (testObject is IVertexed)
                            {
                                for (int a = ((IVertexed)testObject).StartVertex; a < ((IVertexed)testObject).StartVertex + ((IVertexed)testObject).VertexCount; a++)
                                {
                                    CustomVD vertexPass = Vertices[a];
                                    vertexPass.Position = Vector3.Transform(vertexPass.Position, dWorld);
                                    vertexPass.Normal = Vector3.TransformNormal(vertexPass.Normal, dWorld);
                                    Vertices[a] = vertexPass;
                                }
                            }
                        }
                    }
                    // else non-visual entities
                    else
                    {
                        // Update
                        Nullable<ActionType> returnAction = ((IUpdatable)lstUpdatables[i]).Update(tSeconds);
                        if (returnAction.HasValue)
                        {
                            switch (returnAction.Value)
                            {
                                case ActionType.Animation:
                                    SetAnimation(((Animation)lstUpdatables[i]).PlayPosition, lstUpdatables[i]);
                                    break;
                                case ActionType.Trigger:
                                    ActivateObject(((Trigger)lstUpdatables[i]).TargetObject);
                                    break;
                            }
                        }
                    }
                    // Check for self-deactivation
                    if ((lstUpdatables[i] is IActivatable) && !((IActivatable)lstUpdatables[i]).IsActive)
                    { lstUpdatables.RemoveAt(i); i--; }
                }
            }

        } // end Sector
    } // end Map
}